1 //! Mock implementation of `std::thread`.
2
3 pub use crate::rt::thread::AccessError;
4 pub use crate::rt::yield_now;
5 use crate::rt::{self, Execution};
6
7 pub use std::thread::panicking;
8
9 use std::marker::PhantomData;
10 use std::sync::{Arc, Mutex};
11 use std::{fmt, io};
12
13 /// Mock implementation of `std::thread::JoinHandle`.
14 pub struct JoinHandle<T> {
15 result: Arc<Mutex<Option<std::thread::Result<T>>>>,
16 notify: rt::Notify,
17 thread: Thread,
18 }
19
20 /// Mock implementation of `std::thread::Thread`.
21 #[derive(Clone, Debug)]
22 pub struct Thread {
23 id: ThreadId,
24 name: Option<String>,
25 }
26
27 impl Thread {
28 /// Returns a unique identifier for this thread
id(&self) -> ThreadId29 pub fn id(&self) -> ThreadId {
30 self.id
31 }
32
33 /// Returns the (optional) name of this thread
name(&self) -> Option<&str>34 pub fn name(&self) -> Option<&str> {
35 self.name.as_ref().map(|s| s.as_str())
36 }
37 }
38
39 /// Mock implementation of `std::thread::ThreadId`.
40 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
41 pub struct ThreadId {
42 id: crate::rt::thread::Id,
43 }
44
45 impl std::fmt::Debug for ThreadId {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(f, "ThreadId({})", self.id.public_id())
48 }
49 }
50
51 /// Mock implementation of `std::thread::LocalKey`.
52 pub struct LocalKey<T> {
53 // Sadly, these fields have to be public, since function pointers in const
54 // fns are unstable. When fn pointer arguments to const fns stabilize, these
55 // should be made private and replaced with a `const fn new`.
56 //
57 // User code should not rely on the existence of these fields.
58 #[doc(hidden)]
59 pub init: fn() -> T,
60 #[doc(hidden)]
61 pub _p: PhantomData<fn(T)>,
62 }
63
64 /// Thread factory, which can be used in order to configure the properties of
65 /// a new thread.
66 #[derive(Debug)]
67 pub struct Builder {
68 name: Option<String>,
69 }
70
71 static CURRENT_THREAD_KEY: LocalKey<Thread> = LocalKey {
72 init: || unreachable!(),
73 _p: PhantomData,
74 };
75
init_current(execution: &mut Execution, name: Option<String>) -> Thread76 fn init_current(execution: &mut Execution, name: Option<String>) -> Thread {
77 let id = execution.threads.active_id();
78 let thread = Thread {
79 id: ThreadId { id },
80 name,
81 };
82
83 execution
84 .threads
85 .local_init(&CURRENT_THREAD_KEY, thread.clone());
86
87 thread
88 }
89
90 /// Returns a handle to the current thread.
current() -> Thread91 pub fn current() -> Thread {
92 rt::execution(|execution| {
93 let thread = execution.threads.local(&CURRENT_THREAD_KEY);
94 if let Some(thread) = thread {
95 thread.unwrap().clone()
96 } else {
97 // Lazily initialize the current() Thread. This is done to help
98 // handle the initial (unnamed) bootstrap thread.
99 init_current(execution, None)
100 }
101 })
102 }
103
104 /// Mock implementation of `std::thread::spawn`.
spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T, F: 'static, T: 'static,105 pub fn spawn<F, T>(f: F) -> JoinHandle<T>
106 where
107 F: FnOnce() -> T,
108 F: 'static,
109 T: 'static,
110 {
111 spawn_internal(f, None)
112 }
113
spawn_internal<F, T>(f: F, name: Option<String>) -> JoinHandle<T> where F: FnOnce() -> T, F: 'static, T: 'static,114 fn spawn_internal<F, T>(f: F, name: Option<String>) -> JoinHandle<T>
115 where
116 F: FnOnce() -> T,
117 F: 'static,
118 T: 'static,
119 {
120 let result = Arc::new(Mutex::new(None));
121 let notify = rt::Notify::new(true, false);
122
123 let id = {
124 let name = name.clone();
125 let result = result.clone();
126 rt::spawn(move || {
127 rt::execution(|execution| {
128 init_current(execution, name);
129 });
130
131 *result.lock().unwrap() = Some(Ok(f()));
132 notify.notify();
133 })
134 };
135
136 JoinHandle {
137 result,
138 notify,
139 thread: Thread {
140 id: ThreadId { id },
141 name,
142 },
143 }
144 }
145
146 impl Builder {
147 /// Generates the base configuration for spawning a thread, from which
148 /// configuration methods can be chained.
new() -> Builder149 pub fn new() -> Builder {
150 Builder { name: None }
151 }
152
153 /// Names the thread-to-be. Currently the name is used for identification
154 /// only in panic messages.
name(mut self, name: String) -> Builder155 pub fn name(mut self, name: String) -> Builder {
156 self.name = Some(name);
157
158 self
159 }
160
161 /// Sets the size of the stack (in bytes) for the new thread.
stack_size(self, _size: usize) -> Builder162 pub fn stack_size(self, _size: usize) -> Builder {
163 self
164 }
165
166 /// Spawns a new thread by taking ownership of the `Builder`, and returns an
167 /// `io::Result` to its `JoinHandle`.
spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static,168 pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
169 where
170 F: FnOnce() -> T,
171 F: Send + 'static,
172 T: Send + 'static,
173 {
174 Ok(spawn_internal(f, self.name))
175 }
176 }
177
178 impl<T> JoinHandle<T> {
179 /// Waits for the associated thread to finish.
join(self) -> std::thread::Result<T>180 pub fn join(self) -> std::thread::Result<T> {
181 self.notify.wait();
182 self.result.lock().unwrap().take().unwrap()
183 }
184
185 /// Gets a handle to the underlying [`Thread`]
thread(&self) -> &Thread186 pub fn thread(&self) -> &Thread {
187 &self.thread
188 }
189 }
190
191 impl<T: fmt::Debug> fmt::Debug for JoinHandle<T> {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result192 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
193 fmt.debug_struct("JoinHandle").finish()
194 }
195 }
196
_assert_traits()197 fn _assert_traits() {
198 fn assert<T: Send + Sync>() {}
199
200 assert::<JoinHandle<()>>();
201 }
202
203 impl<T: 'static> LocalKey<T> {
204 /// Mock implementation of `std::thread::LocalKey::with`.
with<F, R>(&'static self, f: F) -> R where F: FnOnce(&T) -> R,205 pub fn with<F, R>(&'static self, f: F) -> R
206 where
207 F: FnOnce(&T) -> R,
208 {
209 self.try_with(f)
210 .expect("cannot access a (mock) TLS value during or after it is destroyed")
211 }
212
213 /// Mock implementation of `std::thread::LocalKey::try_with`.
try_with<F, R>(&'static self, f: F) -> Result<R, AccessError> where F: FnOnce(&T) -> R,214 pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
215 where
216 F: FnOnce(&T) -> R,
217 {
218 let value = match unsafe { self.get() } {
219 Some(v) => v?,
220 None => {
221 // Init the value out of the `rt::execution`
222 let value = (self.init)();
223
224 rt::execution(|execution| {
225 execution.threads.local_init(self, value);
226 });
227
228 unsafe { self.get() }.expect("bug")?
229 }
230 };
231 Ok(f(value))
232 }
233
get(&'static self) -> Option<Result<&T, AccessError>>234 unsafe fn get(&'static self) -> Option<Result<&T, AccessError>> {
235 unsafe fn transmute_lt<'a, 'b, T>(t: &'a T) -> &'b T {
236 std::mem::transmute::<&'a T, &'b T>(t)
237 }
238
239 rt::execution(|execution| {
240 let res = execution.threads.local(self)?;
241
242 let local = match res {
243 Ok(l) => l,
244 Err(e) => return Some(Err(e)),
245 };
246
247 // This is, sadly, necessary to allow nested `with` blocks to access
248 // different thread locals. The borrow on the thread-local needs to
249 // escape the lifetime of the borrow on `execution`, since
250 // `rt::execution` mutably borrows a RefCell, and borrowing it twice will
251 // cause a panic. This should be safe, as we know the function being
252 // passed the thread local will not outlive the thread on which
253 // it's executing, by construction --- it's just kind of unfortunate.
254 Some(Ok(transmute_lt(local)))
255 })
256 }
257 }
258
259 impl<T: 'static> fmt::Debug for LocalKey<T> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 f.pad("LocalKey { .. }")
262 }
263 }
264