Skip to main content

lean_ctx/server/
bounded_lock.rs

1use std::sync::Arc;
2use std::time::Duration;
3use tokio::sync::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock};
4
5const BASE_READ_TIMEOUT: Duration = Duration::from_secs(10);
6const BASE_WRITE_TIMEOUT: Duration = Duration::from_secs(10);
7
8/// Acquire a read lock with an adaptive timeout based on I/O health.
9/// Returns `None` on timeout (caller must provide graceful fallback).
10/// Records a freeze event for self-healing if the timeout is hit.
11pub fn read<T: Send + Sync + 'static>(
12    lock: &Arc<RwLock<T>>,
13    context: &str,
14) -> Option<OwnedRwLockReadGuard<T>> {
15    let timeout = crate::core::io_health::adaptive_timeout(BASE_READ_TIMEOUT);
16    let lock_clone = lock.clone();
17    let result = tokio::task::block_in_place(|| {
18        let rt = tokio::runtime::Handle::current();
19        rt.block_on(tokio::time::timeout(timeout, lock_clone.read_owned()))
20    });
21    if let Ok(guard) = result {
22        Some(guard)
23    } else {
24        crate::core::io_health::record_freeze();
25        tracing::warn!(
26            "bounded_lock: read timeout ({}ms) for {context}; degrading gracefully",
27            timeout.as_millis()
28        );
29        None
30    }
31}
32
33/// Acquire a write lock with an adaptive timeout based on I/O health.
34/// Returns `None` on timeout (caller must provide graceful fallback).
35/// Records a freeze event for self-healing if the timeout is hit.
36pub fn write<T: Send + Sync + 'static>(
37    lock: &Arc<RwLock<T>>,
38    context: &str,
39) -> Option<OwnedRwLockWriteGuard<T>> {
40    let timeout = crate::core::io_health::adaptive_timeout(BASE_WRITE_TIMEOUT);
41    let lock_clone = lock.clone();
42    let result = tokio::task::block_in_place(|| {
43        let rt = tokio::runtime::Handle::current();
44        rt.block_on(tokio::time::timeout(timeout, lock_clone.write_owned()))
45    });
46    if let Ok(guard) = result {
47        Some(guard)
48    } else {
49        crate::core::io_health::record_freeze();
50        tracing::warn!(
51            "bounded_lock: write timeout ({}ms) for {context}; degrading gracefully",
52            timeout.as_millis()
53        );
54        None
55    }
56}