In a coroutine-based environment, using standard Thread Local Storage (TLS) via the thread_local! macro is unsafe for storing state that should be specific to a coroutine. Because a coroutine can be rescheduled onto different threads during its lifecycle, accessing TLS can lead to inconsistent or outdated values.
To ensure each coroutine has its own unique local storage, use Coroutine Local Storage (CLS) provided by the coroutine_local! macro.
Key behaviors of CLS:
- Coroutine Context: Guarantees unique storage per coroutine.
- Thread Context: If you access a CLS variable from a standard thread context, it safely falls back to its TLS storage based on the provided key.
To migrate from TLS to CLS in Rust, replace the thread_local! macro with coroutine_local!.
fn coroutine_local_many() {
use std::sync::atomic::{AtomicUsize, Ordering};
coroutine_local!(static FOO: AtomicUsize = AtomicUsize::new(0));
coroutine::scope(|scope| {
for i in 0..10 {
go!(scope, move || {
FOO.with(|f| {
assert_eq!(f.load(Ordering::Relaxed), 0);
f.store(i, Ordering::Relaxed);
assert_eq!(f.load(Ordering::Relaxed), i);
});
});
}
});
// called in thread
FOO.with(|f| {
assert_eq!(f.load(Ordering::Relaxed), 0);
});
}