Commit 9c963ee
committed
Auto merge of rust-lang#148799 - ohadravid:windows-thread-local-dtors-using-fls, r=ChrisDenton
Switch the destructors implementation for thread locals on Windows to use FLS
## Summary
Switch the thread local **destructors** implementation on Windows to use the _Fiber Local Storage_ APIs, which provide native support for setting a callback to be called on thread termination, replacing the current `tls_callback` symbol-based implementation.
_Except for some spellchecking, no LLMs were used to produce code / comments / text in this PR._
## Current Implementation
On Windows, in order to support thread locals with destructors,
the standard library uses a special `tls_callback` symbol that is used to call the `destructors::run()` hook on thread termination.
This has two downsides:
1. It is not well documented, and seems to cause some problems [1] [2] [3].
2. It disallows some synchronization operations, as mentioned in [`LocalKey`'s documentation](https://doc.rust-lang.org/std/thread/struct.LocalKey.html#synchronization-in-thread-local-destructors).
[1]: rust-lang#144234
[2]: rust-lang#145154
[3]: rust-lang#140798
as an example of point 2, this code, which uses `JoinHandle::join` in a thread local Drop impl, will deadlock on stable:
<details>
<summary>Join-on-Drop Deadlock Example</summary>
```rust
struct JoinOnDrop(Option<JoinHandle<()>>);
impl Drop for JoinOnDrop {
fn drop(&mut self) {
self.0.take().unwrap().join().unwrap();
}
}
thread_local! {
static HANDLE: JoinOnDrop = {
let thread = std::thread::spawn(|| {
println!("Starting...");
// std::thread::sleep(Duration::from_secs(3));
println!("Done");
});
JoinOnDrop(Some(thread))
};
}
fn main() {
let thread = std::thread::spawn(|| {
HANDLE.with(|_| {
println!("Some other thread");
})
});
thread.join().unwrap();
println!("Done");
}
```
</details>
## Proposed Change
We can use the `Fls{Alloc,Set,Get,Free}` functions (see https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=102989)
to implement the dtor callback needed for thread locals that have a Drop implementation.
We allocate a single key, and use its destructor callback to run all the registered destructors when a thread is shutting down.
With this implementation, the above code sample will not deadlock (but it still might not be a good idea to do this!).
## Safety and Compatibility
We use the common `thread_local` + atomic pattern to only set a single FLS key.
The destructor callback is only called when that value is non-zero.
Destructors will only run at thread exit: we verify that we are not running in a fiber during the destructors callback. **This means that using fibers (which is very rare) will result in thread locals being leaked**, unless the fiber is converted back to a thread using `ConvertFiberToThread` before thread termination. This is not ideal, but should be OK as destructors are not guaranteed to run, but it needs to be documented.
It might be possible for the user to use something like the current `tls_callback` to observe an already-freed thread locals, which is something that can also happen in the current implementation.
~Destructors will only run on the correct thread: Fibers cannot be moved between threads.~
Destructors will only run on the correct thread: the hook uses a `#[thread_local]` list, so fiber movement between threads does not change which which thread executes the destructors.
Destructors will only run once: even if the hook is called multiple time, the `#[thread_local]` list is cleared after the first run.
Users cannot observe different locals because they are using fibers: because we only use an Fls local marker to trigger the destructors callback, we don't change anything about how users interact with "normal" thread locals and fiber locals.
### DLL Unloading
It is possible to build a `cdylib` which uses thread locals and unload it dynamically using `FreeLibrary`. This can cause the OS to call into an unmapped cleanup hook, so we use `atexit` to manually free the special FLS key, which will also trigger the cleanup hook for each registered thread. This is safe because similar to thread shutdown, no user code can ran after this point, and only the destructors of the running thread will run.
see `tests/run-make/dynamic-loading-cdylib/load_and_unload.rs`.
## Other Notes
The implementation is based on the `key::racy` and `guard::apple` code, because we need a `LazyKey`-like racey static and an `enable` function.
While TLS slots are [limited to 1088](https://devblogs.microsoft.com/oldnewthing/20170712-00/?p=96585), FLS slots are currently [limited to 4000](https://devblogs.microsoft.com/windows-music-dev/effectively-removing-the-fls-slot-allocation-limit-in-windows-10/) per process.
### Miri
Because miri is aware to the thread local implementation, I also implemented these functions and support for them in the interpreter here:
https://github.com/rust-lang/miri/compare/master...ohadravid:miri:windows-fls-support?expand=1
I guess that this will need to be merged before this PR (if this is accepted) - let me know and I'll open that PR as well.
### Targets without `target_thread_local`
In `*-gnu` Windows targets, the `target_thread_local` feature is unavailable.
We could also change the "key" (non-`target_thread_local`) Windows impl at
`library\std\src\sys\thread_local\key\windows.rs`
to be based on the Fls functions. I can add it to this PR, or as a separate PR, if you think this is preferable.
`Cell` in a `#[thread_local]` is used to store the resulting key, like the other implementations. When `target_thread_local` isn't available, we always fetch the atomic and set the FLS key's value.15 files changed
Lines changed: 875 additions & 113 deletions
File tree
- library/std
- src
- sys
- pal/windows
- c
- thread_local/guard
- thread
- tests/thread_local
- src/tools/miri
- src/shims
- windows
- tests/pass/tls
- tests/run-make/dynamic-loading-cdylib
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
241 | 241 | | |
242 | 242 | | |
243 | 243 | | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2130 | 2130 | | |
2131 | 2131 | | |
2132 | 2132 | | |
| 2133 | + | |
| 2134 | + | |
| 2135 | + | |
| 2136 | + | |
| 2137 | + | |
2133 | 2138 | | |
2134 | 2139 | | |
2135 | 2140 | | |
| |||
2258 | 2263 | | |
2259 | 2264 | | |
2260 | 2265 | | |
| 2266 | + | |
2261 | 2267 | | |
2262 | 2268 | | |
2263 | 2269 | | |
| |||
2318 | 2324 | | |
2319 | 2325 | | |
2320 | 2326 | | |
| 2327 | + | |
2321 | 2328 | | |
2322 | 2329 | | |
2323 | 2330 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
30 | 34 | | |
31 | 35 | | |
32 | 36 | | |
| |||
67 | 71 | | |
68 | 72 | | |
69 | 73 | | |
| 74 | + | |
70 | 75 | | |
71 | 76 | | |
72 | 77 | | |
| |||
2667 | 2672 | | |
2668 | 2673 | | |
2669 | 2674 | | |
| 2675 | + | |
2670 | 2676 | | |
2671 | 2677 | | |
2672 | 2678 | | |
| |||
3037 | 3043 | | |
3038 | 3044 | | |
3039 | 3045 | | |
| 3046 | + | |
| 3047 | + | |
3040 | 3048 | | |
3041 | 3049 | | |
3042 | 3050 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | | - | |
9 | | - | |
| 3 | + | |
| 4 | + | |
10 | 5 | | |
11 | | - | |
12 | | - | |
| 6 | + | |
| 7 | + | |
13 | 8 | | |
14 | 9 | | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
| 10 | + | |
65 | 11 | | |
66 | 12 | | |
| 13 | + | |
67 | 14 | | |
| 15 | + | |
68 | 16 | | |
69 | | - | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
70 | 27 | | |
71 | | - | |
72 | | - | |
73 | | - | |
| 28 | + | |
74 | 29 | | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
86 | 100 | | |
87 | 101 | | |
88 | | - | |
89 | | - | |
90 | | - | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
91 | 108 | | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
99 | 119 | | |
100 | | - | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
101 | 129 | | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
102 | 208 | | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
103 | 218 | | |
0 commit comments