]> git.proxmox.com Git - rustc.git/blob - src/libstd/thread/local.rs
f2435b60d8e2847a01bbb67c5d90e56d7a4e6604
[rustc.git] / src / libstd / thread / local.rs
1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Thread local storage
12
13 #![unstable(feature = "thread_local_internals")]
14
15 use prelude::v1::*;
16
17 use cell::UnsafeCell;
18
19 // Sure wish we had macro hygiene, no?
20 #[doc(hidden)]
21 pub mod __impl {
22 pub use super::imp::Key as KeyInner;
23 pub use super::imp::destroy_value;
24 pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER;
25 pub use sys_common::thread_local::StaticKey as OsStaticKey;
26 }
27
28 /// A thread local storage key which owns its contents.
29 ///
30 /// This key uses the fastest possible implementation available to it for the
31 /// target platform. It is instantiated with the `thread_local!` macro and the
32 /// primary method is the `with` method.
33 ///
34 /// The `with` method yields a reference to the contained value which cannot be
35 /// sent across threads or escape the given closure.
36 ///
37 /// # Initialization and Destruction
38 ///
39 /// Initialization is dynamically performed on the first call to `with()`
40 /// within a thread, and values support destructors which will be run when a
41 /// thread exits.
42 ///
43 /// # Examples
44 ///
45 /// ```
46 /// use std::cell::RefCell;
47 /// use std::thread;
48 ///
49 /// thread_local!(static FOO: RefCell<u32> = RefCell::new(1));
50 ///
51 /// FOO.with(|f| {
52 /// assert_eq!(*f.borrow(), 1);
53 /// *f.borrow_mut() = 2;
54 /// });
55 ///
56 /// // each thread starts out with the initial value of 1
57 /// thread::spawn(move|| {
58 /// FOO.with(|f| {
59 /// assert_eq!(*f.borrow(), 1);
60 /// *f.borrow_mut() = 3;
61 /// });
62 /// });
63 ///
64 /// // we retain our original value of 2 despite the child thread
65 /// FOO.with(|f| {
66 /// assert_eq!(*f.borrow(), 2);
67 /// });
68 /// ```
69 #[stable(feature = "rust1", since = "1.0.0")]
70 pub struct LocalKey<T> {
71 // The key itself may be tagged with #[thread_local], and this `Key` is
72 // stored as a `static`, and it's not valid for a static to reference the
73 // address of another thread_local static. For this reason we kinda wonkily
74 // work around this by generating a shim function which will give us the
75 // address of the inner TLS key at runtime.
76 //
77 // This is trivially devirtualizable by LLVM because we never store anything
78 // to this field and rustc can declare the `static` as constant as well.
79 #[doc(hidden)]
80 pub inner: fn() -> &'static __impl::KeyInner<UnsafeCell<Option<T>>>,
81
82 // initialization routine to invoke to create a value
83 #[doc(hidden)]
84 pub init: fn() -> T,
85 }
86
87 /// Declare a new thread local storage key of type `std::thread::LocalKey`.
88 ///
89 /// See [LocalKey documentation](thread/struct.LocalKey.html) for more information.
90 #[macro_export]
91 #[stable(feature = "rust1", since = "1.0.0")]
92 #[allow_internal_unstable]
93 macro_rules! thread_local {
94 (static $name:ident: $t:ty = $init:expr) => (
95 static $name: ::std::thread::LocalKey<$t> = {
96 use std::cell::UnsafeCell as __UnsafeCell;
97 use std::thread::__local::KeyInner as __KeyInner;
98 use std::option::Option as __Option;
99 use std::option::Option::None as __None;
100
101 __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = {
102 __UnsafeCell { value: __None }
103 });
104 fn __init() -> $t { $init }
105 fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> {
106 &__KEY
107 }
108 ::std::thread::LocalKey { inner: __getit, init: __init }
109 };
110 );
111 (pub static $name:ident: $t:ty = $init:expr) => (
112 pub static $name: ::std::thread::LocalKey<$t> = {
113 use std::cell::UnsafeCell as __UnsafeCell;
114 use std::thread::__local::KeyInner as __KeyInner;
115 use std::option::Option as __Option;
116 use std::option::Option::None as __None;
117
118 __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = {
119 __UnsafeCell { value: __None }
120 });
121 fn __init() -> $t { $init }
122 fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> {
123 &__KEY
124 }
125 ::std::thread::LocalKey { inner: __getit, init: __init }
126 };
127 );
128 }
129
130 // Macro pain #4586:
131 //
132 // When cross compiling, rustc will load plugins and macros from the *host*
133 // platform before search for macros from the target platform. This is primarily
134 // done to detect, for example, plugins. Ideally the macro below would be
135 // defined once per module below, but unfortunately this means we have the
136 // following situation:
137 //
138 // 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro
139 // will inject #[thread_local] statics.
140 // 2. We then try to compile a program for arm-linux-androideabi
141 // 3. The compiler has a host of linux and a target of android, so it loads
142 // macros from the *linux* libstd.
143 // 4. The macro generates a #[thread_local] field, but the android libstd does
144 // not use #[thread_local]
145 // 5. Compile error about structs with wrong fields.
146 //
147 // To get around this, we're forced to inject the #[cfg] logic into the macro
148 // itself. Woohoo.
149
150 #[macro_export]
151 #[doc(hidden)]
152 #[allow_internal_unstable]
153 #[cfg(not(no_elf_tls))]
154 macro_rules! __thread_local_inner {
155 (static $name:ident: $t:ty = $init:expr) => (
156 #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
157 not(target_arch = "aarch64")),
158 thread_local)]
159 static $name: ::std::thread::__local::KeyInner<$t> =
160 __thread_local_inner!($init, $t);
161 );
162 (pub static $name:ident: $t:ty = $init:expr) => (
163 #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
164 not(target_arch = "aarch64")),
165 thread_local)]
166 pub static $name: ::std::thread::__local::KeyInner<$t> =
167 __thread_local_inner!($init, $t);
168 );
169 ($init:expr, $t:ty) => ({
170 #[cfg(all(any(target_os = "macos", target_os = "linux"), not(target_arch = "aarch64")))]
171 const _INIT: ::std::thread::__local::KeyInner<$t> = {
172 ::std::thread::__local::KeyInner {
173 inner: ::std::cell::UnsafeCell { value: $init },
174 dtor_registered: ::std::cell::UnsafeCell { value: false },
175 dtor_running: ::std::cell::UnsafeCell { value: false },
176 }
177 };
178
179 #[allow(trivial_casts)]
180 #[cfg(any(not(any(target_os = "macos", target_os = "linux")), target_arch = "aarch64"))]
181 const _INIT: ::std::thread::__local::KeyInner<$t> = {
182 ::std::thread::__local::KeyInner {
183 inner: ::std::cell::UnsafeCell { value: $init },
184 os: ::std::thread::__local::OsStaticKey {
185 inner: ::std::thread::__local::OS_INIT_INNER,
186 dtor: ::std::option::Option::Some(
187 ::std::thread::__local::destroy_value::<$t>
188 ),
189 },
190 }
191 };
192
193 _INIT
194 });
195 }
196
197 #[macro_export]
198 #[doc(hidden)]
199 #[allow_internal_unstable]
200 #[cfg(no_elf_tls)]
201 macro_rules! __thread_local_inner {
202 (static $name:ident: $t:ty = $init:expr) => (
203 static $name: ::std::thread::__local::KeyInner<$t> =
204 __thread_local_inner!($init, $t);
205 );
206 (pub static $name:ident: $t:ty = $init:expr) => (
207 pub static $name: ::std::thread::__local::KeyInner<$t> =
208 __thread_local_inner!($init, $t);
209 );
210 ($init:expr, $t:ty) => ({
211 #[allow(trivial_casts)]
212 const _INIT: ::std::thread::__local::KeyInner<$t> = {
213 ::std::thread::__local::KeyInner {
214 inner: ::std::cell::UnsafeCell { value: $init },
215 os: ::std::thread::__local::OsStaticKey {
216 inner: ::std::thread::__local::OS_INIT_INNER,
217 dtor: ::std::option::Option::Some(
218 ::std::thread::__local::destroy_value::<$t>
219 ),
220 },
221 }
222 };
223
224 _INIT
225 });
226 }
227
228 /// Indicator of the state of a thread local storage key.
229 #[unstable(feature = "std_misc",
230 reason = "state querying was recently added")]
231 #[derive(Eq, PartialEq, Copy, Clone)]
232 pub enum LocalKeyState {
233 /// All keys are in this state whenever a thread starts. Keys will
234 /// transition to the `Valid` state once the first call to `with` happens
235 /// and the initialization expression succeeds.
236 ///
237 /// Keys in the `Uninitialized` state will yield a reference to the closure
238 /// passed to `with` so long as the initialization routine does not panic.
239 Uninitialized,
240
241 /// Once a key has been accessed successfully, it will enter the `Valid`
242 /// state. Keys in the `Valid` state will remain so until the thread exits,
243 /// at which point the destructor will be run and the key will enter the
244 /// `Destroyed` state.
245 ///
246 /// Keys in the `Valid` state will be guaranteed to yield a reference to the
247 /// closure passed to `with`.
248 Valid,
249
250 /// When a thread exits, the destructors for keys will be run (if
251 /// necessary). While a destructor is running, and possibly after a
252 /// destructor has run, a key is in the `Destroyed` state.
253 ///
254 /// Keys in the `Destroyed` states will trigger a panic when accessed via
255 /// `with`.
256 Destroyed,
257 }
258
259 impl<T: 'static> LocalKey<T> {
260 /// Acquires a reference to the value in this TLS key.
261 ///
262 /// This will lazily initialize the value if this thread has not referenced
263 /// this key yet.
264 ///
265 /// # Panics
266 ///
267 /// This function will `panic!()` if the key currently has its
268 /// destructor running, and it **may** panic if the destructor has
269 /// previously been run for this thread.
270 #[stable(feature = "rust1", since = "1.0.0")]
271 pub fn with<F, R>(&'static self, f: F) -> R
272 where F: FnOnce(&T) -> R {
273 let slot = (self.inner)();
274 unsafe {
275 let slot = slot.get().expect("cannot access a TLS value during or \
276 after it is destroyed");
277 f(match *slot.get() {
278 Some(ref inner) => inner,
279 None => self.init(slot),
280 })
281 }
282 }
283
284 unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T {
285 // Execute the initialization up front, *then* move it into our slot,
286 // just in case initialization fails.
287 let value = (self.init)();
288 let ptr = slot.get();
289 *ptr = Some(value);
290 (*ptr).as_ref().unwrap()
291 }
292
293 /// Query the current state of this key.
294 ///
295 /// A key is initially in the `Uninitialized` state whenever a thread
296 /// starts. It will remain in this state up until the first call to `with`
297 /// within a thread has run the initialization expression successfully.
298 ///
299 /// Once the initialization expression succeeds, the key transitions to the
300 /// `Valid` state which will guarantee that future calls to `with` will
301 /// succeed within the thread.
302 ///
303 /// When a thread exits, each key will be destroyed in turn, and as keys are
304 /// destroyed they will enter the `Destroyed` state just before the
305 /// destructor starts to run. Keys may remain in the `Destroyed` state after
306 /// destruction has completed. Keys without destructors (e.g. with types
307 /// that are `Copy`), may never enter the `Destroyed` state.
308 ///
309 /// Keys in the `Uninitialized` can be accessed so long as the
310 /// initialization does not panic. Keys in the `Valid` state are guaranteed
311 /// to be able to be accessed. Keys in the `Destroyed` state will panic on
312 /// any call to `with`.
313 #[unstable(feature = "std_misc",
314 reason = "state querying was recently added")]
315 pub fn state(&'static self) -> LocalKeyState {
316 unsafe {
317 match (self.inner)().get() {
318 Some(cell) => {
319 match *cell.get() {
320 Some(..) => LocalKeyState::Valid,
321 None => LocalKeyState::Uninitialized,
322 }
323 }
324 None => LocalKeyState::Destroyed,
325 }
326 }
327 }
328 }
329
330 #[cfg(all(any(target_os = "macos", target_os = "linux"),
331 not(target_arch = "aarch64"),
332 not(no_elf_tls)))]
333 #[doc(hidden)]
334 mod imp {
335 use prelude::v1::*;
336
337 use cell::UnsafeCell;
338 use intrinsics;
339 use ptr;
340
341 pub struct Key<T> {
342 // Place the inner bits in an `UnsafeCell` to currently get around the
343 // "only Sync statics" restriction. This allows any type to be placed in
344 // the cell.
345 //
346 // Note that all access requires `T: 'static` so it can't be a type with
347 // any borrowed pointers still.
348 pub inner: UnsafeCell<T>,
349
350 // Metadata to keep track of the state of the destructor. Remember that
351 // these variables are thread-local, not global.
352 pub dtor_registered: UnsafeCell<bool>, // should be Cell
353 pub dtor_running: UnsafeCell<bool>, // should be Cell
354 }
355
356 unsafe impl<T> ::marker::Sync for Key<T> { }
357
358 impl<T> Key<T> {
359 pub unsafe fn get(&'static self) -> Option<&'static T> {
360 if intrinsics::needs_drop::<T>() && *self.dtor_running.get() {
361 return None
362 }
363 self.register_dtor();
364 Some(&*self.inner.get())
365 }
366
367 unsafe fn register_dtor(&self) {
368 if !intrinsics::needs_drop::<T>() || *self.dtor_registered.get() {
369 return
370 }
371
372 register_dtor(self as *const _ as *mut u8,
373 destroy_value::<T>);
374 *self.dtor_registered.get() = true;
375 }
376 }
377
378 // Since what appears to be glibc 2.18 this symbol has been shipped which
379 // GCC and clang both use to invoke destructors in thread_local globals, so
380 // let's do the same!
381 //
382 // Note, however, that we run on lots older linuxes, as well as cross
383 // compiling from a newer linux to an older linux, so we also have a
384 // fallback implementation to use as well.
385 //
386 // Due to rust-lang/rust#18804, make sure this is not generic!
387 #[cfg(target_os = "linux")]
388 unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
389 use boxed;
390 use mem;
391 use libc;
392 use sys_common::thread_local as os;
393
394 extern {
395 #[linkage = "extern_weak"]
396 static __dso_handle: *mut u8;
397 #[linkage = "extern_weak"]
398 static __cxa_thread_atexit_impl: *const ();
399 }
400 if !__cxa_thread_atexit_impl.is_null() {
401 type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
402 arg: *mut u8,
403 dso_handle: *mut u8) -> libc::c_int;
404 mem::transmute::<*const (), F>(__cxa_thread_atexit_impl)
405 (dtor, t, &__dso_handle as *const _ as *mut _);
406 return
407 }
408
409 // The fallback implementation uses a vanilla OS-based TLS key to track
410 // the list of destructors that need to be run for this thread. The key
411 // then has its own destructor which runs all the other destructors.
412 //
413 // The destructor for DTORS is a little special in that it has a `while`
414 // loop to continuously drain the list of registered destructors. It
415 // *should* be the case that this loop always terminates because we
416 // provide the guarantee that a TLS key cannot be set after it is
417 // flagged for destruction.
418 static DTORS: os::StaticKey = os::StaticKey {
419 inner: os::INIT_INNER,
420 dtor: Some(run_dtors as unsafe extern "C" fn(*mut u8)),
421 };
422 type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
423 if DTORS.get().is_null() {
424 let v: Box<List> = box Vec::new();
425 DTORS.set(boxed::into_raw(v) as *mut u8);
426 }
427 let list: &mut List = &mut *(DTORS.get() as *mut List);
428 list.push((t, dtor));
429
430 unsafe extern fn run_dtors(mut ptr: *mut u8) {
431 while !ptr.is_null() {
432 let list: Box<List> = Box::from_raw(ptr as *mut List);
433 for &(ptr, dtor) in &*list {
434 dtor(ptr);
435 }
436 ptr = DTORS.get();
437 DTORS.set(ptr::null_mut());
438 }
439 }
440 }
441
442 // OSX's analog of the above linux function is this _tlv_atexit function.
443 // The disassembly of thread_local globals in C++ (at least produced by
444 // clang) will have this show up in the output.
445 #[cfg(target_os = "macos")]
446 unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
447 extern {
448 fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
449 arg: *mut u8);
450 }
451 _tlv_atexit(dtor, t);
452 }
453
454 pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
455 let ptr = ptr as *mut Key<T>;
456 // Right before we run the user destructor be sure to flag the
457 // destructor as running for this thread so calls to `get` will return
458 // `None`.
459 *(*ptr).dtor_running.get() = true;
460 ptr::read((*ptr).inner.get());
461 }
462 }
463
464 #[cfg(any(not(any(target_os = "macos", target_os = "linux")),
465 target_arch = "aarch64",
466 no_elf_tls))]
467 #[doc(hidden)]
468 mod imp {
469 use prelude::v1::*;
470
471 use alloc::boxed;
472 use cell::UnsafeCell;
473 use mem;
474 use ptr;
475 use sys_common::thread_local::StaticKey as OsStaticKey;
476
477 pub struct Key<T> {
478 // Statically allocated initialization expression, using an `UnsafeCell`
479 // for the same reasons as above.
480 pub inner: UnsafeCell<T>,
481
482 // OS-TLS key that we'll use to key off.
483 pub os: OsStaticKey,
484 }
485
486 unsafe impl<T> ::marker::Sync for Key<T> { }
487
488 struct Value<T: 'static> {
489 key: &'static Key<T>,
490 value: T,
491 }
492
493 impl<T> Key<T> {
494 pub unsafe fn get(&'static self) -> Option<&'static T> {
495 self.ptr().map(|p| &*p)
496 }
497
498 unsafe fn ptr(&'static self) -> Option<*mut T> {
499 let ptr = self.os.get() as *mut Value<T>;
500 if !ptr.is_null() {
501 if ptr as usize == 1 {
502 return None
503 }
504 return Some(&mut (*ptr).value as *mut T);
505 }
506
507 // If the lookup returned null, we haven't initialized our own local
508 // copy, so do that now.
509 //
510 // Also note that this transmute_copy should be ok because the value
511 // `inner` is already validated to be a valid `static` value, so we
512 // should be able to freely copy the bits.
513 let ptr: Box<Value<T>> = box Value {
514 key: self,
515 value: mem::transmute_copy(&self.inner),
516 };
517 let ptr = boxed::into_raw(ptr);
518 self.os.set(ptr as *mut u8);
519 Some(&mut (*ptr).value as *mut T)
520 }
521 }
522
523 pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
524 // The OS TLS ensures that this key contains a NULL value when this
525 // destructor starts to run. We set it back to a sentinel value of 1 to
526 // ensure that any future calls to `get` for this thread will return
527 // `None`.
528 //
529 // Note that to prevent an infinite loop we reset it back to null right
530 // before we return from the destructor ourselves.
531 let ptr = Box::from_raw(ptr as *mut Value<T>);
532 let key = ptr.key;
533 key.os.set(1 as *mut u8);
534 drop(ptr);
535 key.os.set(ptr::null_mut());
536 }
537 }
538
539 #[cfg(test)]
540 mod tests {
541 use prelude::v1::*;
542
543 use sync::mpsc::{channel, Sender};
544 use cell::UnsafeCell;
545 use super::LocalKeyState;
546 use thread;
547
548 struct Foo(Sender<()>);
549
550 impl Drop for Foo {
551 fn drop(&mut self) {
552 let Foo(ref s) = *self;
553 s.send(()).unwrap();
554 }
555 }
556
557 #[test]
558 fn smoke_no_dtor() {
559 thread_local!(static FOO: UnsafeCell<i32> = UnsafeCell { value: 1 });
560
561 FOO.with(|f| unsafe {
562 assert_eq!(*f.get(), 1);
563 *f.get() = 2;
564 });
565 let (tx, rx) = channel();
566 let _t = thread::spawn(move|| {
567 FOO.with(|f| unsafe {
568 assert_eq!(*f.get(), 1);
569 });
570 tx.send(()).unwrap();
571 });
572 rx.recv().unwrap();
573
574 FOO.with(|f| unsafe {
575 assert_eq!(*f.get(), 2);
576 });
577 }
578
579 #[test]
580 fn states() {
581 struct Foo;
582 impl Drop for Foo {
583 fn drop(&mut self) {
584 assert!(FOO.state() == LocalKeyState::Destroyed);
585 }
586 }
587 fn foo() -> Foo {
588 assert!(FOO.state() == LocalKeyState::Uninitialized);
589 Foo
590 }
591 thread_local!(static FOO: Foo = foo());
592
593 thread::spawn(|| {
594 assert!(FOO.state() == LocalKeyState::Uninitialized);
595 FOO.with(|_| {
596 assert!(FOO.state() == LocalKeyState::Valid);
597 });
598 assert!(FOO.state() == LocalKeyState::Valid);
599 }).join().ok().unwrap();
600 }
601
602 #[test]
603 fn smoke_dtor() {
604 thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell {
605 value: None
606 });
607
608 let (tx, rx) = channel();
609 let _t = thread::spawn(move|| unsafe {
610 let mut tx = Some(tx);
611 FOO.with(|f| {
612 *f.get() = Some(Foo(tx.take().unwrap()));
613 });
614 });
615 rx.recv().unwrap();
616 }
617
618 #[test]
619 fn circular() {
620 struct S1;
621 struct S2;
622 thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell {
623 value: None
624 });
625 thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell {
626 value: None
627 });
628 static mut HITS: u32 = 0;
629
630 impl Drop for S1 {
631 fn drop(&mut self) {
632 unsafe {
633 HITS += 1;
634 if K2.state() == LocalKeyState::Destroyed {
635 assert_eq!(HITS, 3);
636 } else {
637 if HITS == 1 {
638 K2.with(|s| *s.get() = Some(S2));
639 } else {
640 assert_eq!(HITS, 3);
641 }
642 }
643 }
644 }
645 }
646 impl Drop for S2 {
647 fn drop(&mut self) {
648 unsafe {
649 HITS += 1;
650 assert!(K1.state() != LocalKeyState::Destroyed);
651 assert_eq!(HITS, 2);
652 K1.with(|s| *s.get() = Some(S1));
653 }
654 }
655 }
656
657 thread::spawn(move|| {
658 drop(S1);
659 }).join().ok().unwrap();
660 }
661
662 #[test]
663 fn self_referential() {
664 struct S1;
665 thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell {
666 value: None
667 });
668
669 impl Drop for S1 {
670 fn drop(&mut self) {
671 assert!(K1.state() == LocalKeyState::Destroyed);
672 }
673 }
674
675 thread::spawn(move|| unsafe {
676 K1.with(|s| *s.get() = Some(S1));
677 }).join().ok().unwrap();
678 }
679
680 #[test]
681 fn dtors_in_dtors_in_dtors() {
682 struct S1(Sender<()>);
683 thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell {
684 value: None
685 });
686 thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell {
687 value: None
688 });
689
690 impl Drop for S1 {
691 fn drop(&mut self) {
692 let S1(ref tx) = *self;
693 unsafe {
694 if K2.state() != LocalKeyState::Destroyed {
695 K2.with(|s| *s.get() = Some(Foo(tx.clone())));
696 }
697 }
698 }
699 }
700
701 let (tx, rx) = channel();
702 let _t = thread::spawn(move|| unsafe {
703 let mut tx = Some(tx);
704 K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
705 });
706 rx.recv().unwrap();
707 }
708 }
709
710 #[cfg(test)]
711 mod dynamic_tests {
712 use prelude::v1::*;
713
714 use cell::RefCell;
715 use collections::HashMap;
716
717 #[test]
718 fn smoke() {
719 fn square(i: i32) -> i32 { i * i }
720 thread_local!(static FOO: i32 = square(3));
721
722 FOO.with(|f| {
723 assert_eq!(*f, 9);
724 });
725 }
726
727 #[test]
728 fn hashmap() {
729 fn map() -> RefCell<HashMap<i32, i32>> {
730 let mut m = HashMap::new();
731 m.insert(1, 2);
732 RefCell::new(m)
733 }
734 thread_local!(static FOO: RefCell<HashMap<i32, i32>> = map());
735
736 FOO.with(|map| {
737 assert_eq!(map.borrow()[&1], 2);
738 });
739 }
740
741 #[test]
742 fn refcell_vec() {
743 thread_local!(static FOO: RefCell<Vec<u32>> = RefCell::new(vec![1, 2, 3]));
744
745 FOO.with(|vec| {
746 assert_eq!(vec.borrow().len(), 3);
747 vec.borrow_mut().push(4);
748 assert_eq!(vec.borrow()[3], 4);
749 });
750 }
751 }