]>
Commit | Line | Data |
---|---|---|
476ff2be SL |
1 | // Copyright 2016 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 | #![cfg(target_thread_local)] | |
12 | #![unstable(feature = "thread_local_internals", issue = "0")] | |
13 | ||
14 | use cell::{Cell, UnsafeCell}; | |
7cac9316 | 15 | use mem; |
476ff2be SL |
16 | use ptr; |
17 | ||
7cac9316 | 18 | |
476ff2be SL |
19 | pub struct Key<T> { |
20 | inner: UnsafeCell<Option<T>>, | |
21 | ||
22 | // Metadata to keep track of the state of the destructor. Remember that | |
23 | // these variables are thread-local, not global. | |
24 | dtor_registered: Cell<bool>, | |
25 | dtor_running: Cell<bool>, | |
26 | } | |
27 | ||
28 | unsafe impl<T> ::marker::Sync for Key<T> { } | |
29 | ||
30 | impl<T> Key<T> { | |
31 | pub const fn new() -> Key<T> { | |
32 | Key { | |
33 | inner: UnsafeCell::new(None), | |
34 | dtor_registered: Cell::new(false), | |
35 | dtor_running: Cell::new(false) | |
36 | } | |
37 | } | |
38 | ||
39 | pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { | |
40 | unsafe { | |
7cac9316 | 41 | if mem::needs_drop::<T>() && self.dtor_running.get() { |
476ff2be SL |
42 | return None |
43 | } | |
44 | self.register_dtor(); | |
45 | } | |
46 | Some(&self.inner) | |
47 | } | |
48 | ||
49 | unsafe fn register_dtor(&self) { | |
7cac9316 | 50 | if !mem::needs_drop::<T>() || self.dtor_registered.get() { |
476ff2be SL |
51 | return |
52 | } | |
53 | ||
54 | register_dtor(self as *const _ as *mut u8, | |
55 | destroy_value::<T>); | |
56 | self.dtor_registered.set(true); | |
57 | } | |
58 | } | |
59 | ||
041b39d2 | 60 | pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { |
476ff2be SL |
61 | // The fallback implementation uses a vanilla OS-based TLS key to track |
62 | // the list of destructors that need to be run for this thread. The key | |
63 | // then has its own destructor which runs all the other destructors. | |
64 | // | |
65 | // The destructor for DTORS is a little special in that it has a `while` | |
66 | // loop to continuously drain the list of registered destructors. It | |
67 | // *should* be the case that this loop always terminates because we | |
68 | // provide the guarantee that a TLS key cannot be set after it is | |
69 | // flagged for destruction. | |
70 | use sys_common::thread_local as os; | |
71 | ||
72 | static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors)); | |
73 | type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; | |
74 | if DTORS.get().is_null() { | |
75 | let v: Box<List> = box Vec::new(); | |
76 | DTORS.set(Box::into_raw(v) as *mut u8); | |
77 | } | |
78 | let list: &mut List = &mut *(DTORS.get() as *mut List); | |
79 | list.push((t, dtor)); | |
80 | ||
81 | unsafe extern fn run_dtors(mut ptr: *mut u8) { | |
82 | while !ptr.is_null() { | |
83 | let list: Box<List> = Box::from_raw(ptr as *mut List); | |
84 | for &(ptr, dtor) in list.iter() { | |
85 | dtor(ptr); | |
86 | } | |
87 | ptr = DTORS.get(); | |
88 | DTORS.set(ptr::null_mut()); | |
89 | } | |
90 | } | |
91 | } | |
92 | ||
93 | pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { | |
94 | let ptr = ptr as *mut Key<T>; | |
95 | // Right before we run the user destructor be sure to flag the | |
96 | // destructor as running for this thread so calls to `get` will return | |
97 | // `None`. | |
98 | (*ptr).dtor_running.set(true); | |
99 | ||
cc61c64b | 100 | // The macOS implementation of TLS apparently had an odd aspect to it |
476ff2be SL |
101 | // where the pointer we have may be overwritten while this destructor |
102 | // is running. Specifically if a TLS destructor re-accesses TLS it may | |
103 | // trigger a re-initialization of all TLS variables, paving over at | |
104 | // least some destroyed ones with initial values. | |
105 | // | |
cc61c64b | 106 | // This means that if we drop a TLS value in place on macOS that we could |
476ff2be SL |
107 | // revert the value to its original state halfway through the |
108 | // destructor, which would be bad! | |
109 | // | |
cc61c64b | 110 | // Hence, we use `ptr::read` on macOS (to move to a "safe" location) |
476ff2be SL |
111 | // instead of drop_in_place. |
112 | if cfg!(target_os = "macos") { | |
113 | ptr::read((*ptr).inner.get()); | |
114 | } else { | |
115 | ptr::drop_in_place((*ptr).inner.get()); | |
116 | } | |
117 | } | |
041b39d2 XL |
118 | |
119 | pub fn requires_move_before_drop() -> bool { | |
120 | false | |
121 | } |