From 93065468858398e50b562c4e0135458fdba5e82d Mon Sep 17 00:00:00 2001 From: ChilePiquin Date: Tue, 15 Sep 2026 10:52:52 -0600 Subject: [PATCH] fix(python): log a warning when shutdown can't confirm a clean exit shutdown_timeout itself returns nothing, so neither of the two ways shutdown() can fail to cleanly retire the runtime had any signal attached: the wait for in-flight work timing out (it forces the retirement attempt anyway, but silently), or Arc::try_unwrap losing the exclusivity race even after that (no owned handle to call shutdown_timeout on, so nothing more to do). Both now log a warning via the `log` crate, already a workspace dependency and already used this way elsewhere in this crate (table.rs's progress-callback error handling). Co-Authored-By: Claude Sonnet 5 --- python/src/runtime.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/python/src/runtime.rs b/python/src/runtime.rs index bcc416444..170f7e592 100644 --- a/python/src/runtime.rs +++ b/python/src/runtime.rs @@ -217,13 +217,22 @@ pub fn block_on(fut: F) -> F::Output { /// `get_runtime()` caller is, at that exact instant, still between loading /// the slot and finishing its own call, this abandons the runtime instead /// of forcing the issue -- the same trade `atfork_child` already makes. +/// +/// Neither of the two ways this can fail to cleanly retire the runtime -- +/// the wait timing out, or `try_unwrap` losing that race -- has any other +/// signal to report through (`shutdown_timeout` itself returns nothing), +/// so both log a warning instead of failing silently. pub fn shutdown(timeout: Duration) { let deadline = Instant::now() + timeout; loop { - if OUTSTANDING.load(Ordering::SeqCst) == 0 { + let outstanding = OUTSTANDING.load(Ordering::SeqCst); + if outstanding == 0 { break; } if Instant::now() >= deadline { + log::warn!( + "lancedb: runtime shutdown timed out with {outstanding} call(s) still in flight; forcing shutdown anyway, some in-flight work may be abandoned" + ); break; } std::thread::sleep(Duration::from_millis(1)); @@ -232,10 +241,22 @@ pub fn shutdown(timeout: Duration) { return; }; RUNTIME.compare_and_swap(&Some(Arc::clone(¤t)), None); - if let Ok(tagged) = Arc::try_unwrap(current) { - tagged - .runtime - .shutdown_timeout(deadline.saturating_duration_since(Instant::now())); + match Arc::try_unwrap(current) { + Ok(tagged) => { + tagged + .runtime + .shutdown_timeout(deadline.saturating_duration_since(Instant::now())); + } + Err(_) => { + // Some transient `get_runtime()` caller is, at this exact + // instant, still between loading the slot and finishing its own + // call: we have no owned handle to call `shutdown_timeout` on, + // and no way to force one (tokio has no shutdown API over a + // shared reference). Nothing more to do but say so. + log::warn!( + "lancedb: runtime shutdown could not obtain exclusive ownership; the shared runtime was left running" + ); + } } }