server: wait for eye hub release before reopen
This commit is contained in:
parent
38df734d42
commit
38ef8327d3
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.12"
|
version = "0.22.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1686,7 +1686,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.12"
|
version = "0.22.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1698,7 +1698,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.12"
|
version = "0.22.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.12"
|
version = "0.22.13"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.12"
|
version = "0.22.13"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.12"
|
version = "0.22.13"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ struct EyeHub {
|
|||||||
tx: broadcast::Sender<VideoPacket>,
|
tx: broadcast::Sender<VideoPacket>,
|
||||||
running: Arc<AtomicBool>,
|
running: Arc<AtomicBool>,
|
||||||
subscribers: Arc<AtomicUsize>,
|
subscribers: Arc<AtomicUsize>,
|
||||||
abort: tokio::task::AbortHandle,
|
task: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
include!("main/handler_startup.rs");
|
include!("main/handler_startup.rs");
|
||||||
|
|||||||
@ -35,20 +35,40 @@ impl EyeHub {
|
|||||||
running_for_task.store(false, Ordering::Relaxed);
|
running_for_task.store(false, Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
tx: tx.clone(),
|
tx: tx.clone(),
|
||||||
running: Arc::clone(&running),
|
running: Arc::clone(&running),
|
||||||
subscribers: Arc::clone(&subscribers),
|
subscribers: Arc::clone(&subscribers),
|
||||||
abort: task.abort_handle(),
|
task: Arc::new(Mutex::new(Some(task))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shutdown(&self) {
|
fn shutdown(&self) {
|
||||||
if self.running.swap(false, Ordering::AcqRel) {
|
if self.running.swap(false, Ordering::AcqRel) {
|
||||||
self.abort.abort();
|
if let Ok(mut task) = self.task.try_lock()
|
||||||
|
&& let Some(task) = task.take()
|
||||||
|
{
|
||||||
|
task.abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn shutdown_and_wait(&self, timeout: Duration) {
|
||||||
|
self.running.store(false, Ordering::Release);
|
||||||
|
let task = self.task.lock().await.take();
|
||||||
|
if let Some(task) = task {
|
||||||
|
task.abort();
|
||||||
|
let _ = tokio::time::timeout(timeout, task).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eye_hub_reopen_grace() -> Duration {
|
||||||
|
std::env::var("LESAVKA_EYE_HUB_REOPEN_GRACE_MS")
|
||||||
|
.ok()
|
||||||
|
.and_then(|value| value.trim().parse::<u64>().ok())
|
||||||
|
.map(Duration::from_millis)
|
||||||
|
.unwrap_or_else(|| Duration::from_millis(250))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_conflicting_eye_hubs(
|
fn take_conflicting_eye_hubs(
|
||||||
|
|||||||
@ -89,7 +89,7 @@ impl Handler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
for hub in stale_hubs {
|
for hub in stale_hubs {
|
||||||
hub.shutdown();
|
hub.shutdown_and_wait(eye_hub_reopen_grace()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lease = self.capture_power.acquire().await;
|
let lease = self.capture_power.acquire().await;
|
||||||
@ -147,6 +147,6 @@ async fn forward_eye_hub_packets(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if subscribers.fetch_sub(1, Ordering::AcqRel) == 1 {
|
if subscribers.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||||
hub_for_task.shutdown();
|
hub_for_task.shutdown_and_wait(eye_hub_reopen_grace()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -135,6 +135,38 @@ mod server_main_eye_hub {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eye_hub_shutdown_and_wait_releases_pending_stream_before_reopen() {
|
||||||
|
let rt = tokio::runtime::Runtime::new().expect("runtime");
|
||||||
|
with_capture_power_disabled(|| {
|
||||||
|
rt.block_on(async {
|
||||||
|
let hub = EyeHub::spawn(
|
||||||
|
stream::pending::<Result<VideoPacket, tonic::Status>>(),
|
||||||
|
CapturePowerManager::new().acquire().await,
|
||||||
|
);
|
||||||
|
hub.shutdown_and_wait(std::time::Duration::from_millis(100))
|
||||||
|
.await;
|
||||||
|
assert!(
|
||||||
|
!hub.running.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
"conflicting eye hubs must be fully shut down before reopening the same V4L2 card"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eye_hub_reopen_grace_defaults_to_v4l2_release_window() {
|
||||||
|
with_var("LESAVKA_EYE_HUB_REOPEN_GRACE_MS", None::<&str>, || {
|
||||||
|
assert_eq!(
|
||||||
|
eye_hub_reopen_grace(),
|
||||||
|
std::time::Duration::from_millis(250)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
with_var("LESAVKA_EYE_HUB_REOPEN_GRACE_MS", Some("75"), || {
|
||||||
|
assert_eq!(eye_hub_reopen_grace(), std::time::Duration::from_millis(75));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(coverage)]
|
#[cfg(coverage)]
|
||||||
fn eye_hub_forwarder_handles_dropped_downstream() {
|
fn eye_hub_forwarder_handles_dropped_downstream() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user