@@ -340,11 +340,50 @@ pub extern "C" fn rr_recording_stream_new(
340
340
}
341
341
}
342
342
343
+ /// See `THREAD_LIFE_TRACKER` for more information.
344
+ struct TrivialTypeWithDrop ;
345
+
346
+ impl Drop for TrivialTypeWithDrop {
347
+ fn drop ( & mut self ) {
348
+ // Try to ensure that drop doesn't get optimized away.
349
+ std:: hint:: black_box ( self ) ;
350
+ }
351
+ }
352
+
353
+ thread_local ! {
354
+ /// It can happen that we end up in here during a thread shutdown.
355
+ /// This happens either when:
356
+ /// * the application shuts down, causing the destructor of globally defined recordings to be invoked
357
+ /// -> Not an issue, we likely already destroyed the recording list.
358
+ /// * the user stored their C++ recording in a thread local variable, and then shut down the thread.
359
+ /// -> More problematic, since we can't access `RECORDING_STREAMS` now, meaning we leak the recording.
360
+ /// (we can't access it because we use channels internally which in turn use thread-local storage)
361
+ ///
362
+ /// So how do we figure out that our thread is shutting down?
363
+ /// As of writing `std::thread::current()` panics if there's nothing on `std::sys_common::thread_info::current_thread()`.
364
+ /// Unfortunately, `std::sys_common` is a private implementation detail!
365
+ /// So instead, we try accessing a thread local variable and see if that's still possible.
366
+ /// If not, then we assume that the thread is shutting down.
367
+ ///
368
+ /// Just any thread local variable will not do though!
369
+ /// We need something that is guaranteed to be dropped with the thread shutting down.
370
+ /// A simple integer value won't do that, `Box` works but seems wasteful, so we use a trivial type with a drop implementation.
371
+ #[ allow( clippy:: unnecessary_box_returns) ]
372
+ pub static THREAD_LIFE_TRACKER : TrivialTypeWithDrop = TrivialTypeWithDrop ;
373
+ }
374
+
343
375
#[ allow( unsafe_code) ]
344
376
#[ no_mangle]
345
377
pub extern "C" fn rr_recording_stream_free ( id : CRecordingStream ) {
346
- if let Some ( stream) = RECORDING_STREAMS . lock ( ) . remove ( id) {
347
- stream. disconnect ( ) ;
378
+ if THREAD_LIFE_TRACKER . try_with ( |_v| { } ) . is_ok ( ) {
379
+ if let Some ( stream) = RECORDING_STREAMS . lock ( ) . remove ( id) {
380
+ stream. disconnect ( ) ;
381
+ }
382
+ } else {
383
+ // Yes, at least as of writing we can still log things in this state!
384
+ re_log:: debug!(
385
+ "rr_recording_stream_free called on a thread that is shutting down and can no longer access thread locals. We can't handle this and have to ignore this call."
386
+ ) ;
348
387
}
349
388
}
350
389
0 commit comments