1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 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 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
//! Mock implementation of `std::thread`.
pub use crate::rt::thread::AccessError;
pub use crate::rt::yield_now;
use crate::rt::{self, Execution, Location};
#[doc(no_inline)]
pub use std::thread::panicking;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use std::{fmt, io};
use tracing::trace;
/// Mock implementation of `std::thread::JoinHandle`.
pub struct JoinHandle<T> {
result: Arc<Mutex<Option<std::thread::Result<T>>>>,
notify: rt::Notify,
thread: Thread,
}
/// Mock implementation of `std::thread::Thread`.
#[derive(Clone, Debug)]
pub struct Thread {
id: ThreadId,
name: Option<String>,
}
impl Thread {
/// Returns a unique identifier for this thread
pub fn id(&self) -> ThreadId {
self.id
}
/// Returns the (optional) name of this thread
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
/// Mock implementation of [`std::thread::Thread::unpark`].
///
/// Atomically makes the handle's token available if it is not already.
///
/// Every thread is equipped with some basic low-level blocking support, via
/// the [`park`] function and the `unpark()` method. These can be
/// used as a more CPU-efficient implementation of a spinlock.
///
/// See the [park documentation][park] for more details.
pub fn unpark(&self) {
rt::execution(|execution| execution.threads.unpark(self.id.id));
}
}
/// Mock implementation of `std::thread::ThreadId`.
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct ThreadId {
id: crate::rt::thread::Id,
}
impl std::fmt::Debug for ThreadId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ThreadId({})", self.id.public_id())
}
}
/// Mock implementation of `std::thread::LocalKey`.
pub struct LocalKey<T> {
// Sadly, these fields have to be public, since function pointers in const
// fns are unstable. When fn pointer arguments to const fns stabilize, these
// should be made private and replaced with a `const fn new`.
//
// User code should not rely on the existence of these fields.
#[doc(hidden)]
pub init: fn() -> T,
#[doc(hidden)]
pub _p: PhantomData<fn(T)>,
}
/// Thread factory, which can be used in order to configure the properties of
/// a new thread.
#[derive(Debug)]
pub struct Builder {
name: Option<String>,
stack_size: Option<usize>,
}
static CURRENT_THREAD_KEY: LocalKey<Thread> = LocalKey {
init: || unreachable!(),
_p: PhantomData,
};
fn init_current(execution: &mut Execution, name: Option<String>) -> Thread {
let id = execution.threads.active_id();
let thread = Thread {
id: ThreadId { id },
name,
};
execution
.threads
.local_init(&CURRENT_THREAD_KEY, thread.clone());
thread
}
/// Returns a handle to the current thread.
pub fn current() -> Thread {
rt::execution(|execution| {
let thread = execution.threads.local(&CURRENT_THREAD_KEY);
if let Some(thread) = thread {
thread.unwrap().clone()
} else {
// Lazily initialize the current() Thread. This is done to help
// handle the initial (unnamed) bootstrap thread.
init_current(execution, None)
}
})
}
/// Mock implementation of `std::thread::spawn`.
///
/// Note that you may only have [`MAX_THREADS`](crate::MAX_THREADS) threads in a given loom tests
/// _including_ the main thread.
#[track_caller]
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: 'static,
T: 'static,
{
spawn_internal(f, None, None, location!())
}
/// Mock implementation of `std::thread::park`.
///
/// Blocks unless or until the current thread's token is made available.
///
/// A call to `park` does not guarantee that the thread will remain parked
/// forever, and callers should be prepared for this possibility.
#[track_caller]
pub fn park() {
rt::park(location!());
}
fn spawn_internal<F, T>(
f: F,
name: Option<String>,
stack_size: Option<usize>,
location: Location,
) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: 'static,
T: 'static,
{
let result = Arc::new(Mutex::new(None));
let notify = rt::Notify::new(true, false);
let id = {
let name = name.clone();
let result = result.clone();
rt::spawn(stack_size, move || {
rt::execution(|execution| {
init_current(execution, name);
});
*result.lock().unwrap() = Some(Ok(f()));
notify.notify(location);
})
};
JoinHandle {
result,
notify,
thread: Thread {
id: ThreadId { id },
name,
},
}
}
impl Builder {
/// Generates the base configuration for spawning a thread, from which
/// configuration methods can be chained.
// `std::thread::Builder` does not implement `Default`, so this type does
// not either, as it's a mock version of the `std` type.
#[allow(clippy::new_without_default)]
pub fn new() -> Builder {
Builder {
name: None,
stack_size: None,
}
}
/// Names the thread-to-be. Currently the name is used for identification
/// only in panic messages.
pub fn name(mut self, name: String) -> Builder {
self.name = Some(name);
self
}
/// Sets the size of the stack (in bytes) for the new thread.
pub fn stack_size(mut self, size: usize) -> Builder {
self.stack_size = Some(size);
self
}
/// Spawns a new thread by taking ownership of the `Builder`, and returns an
/// `io::Result` to its `JoinHandle`.
#[track_caller]
pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
Ok(spawn_internal(f, self.name, self.stack_size, location!()))
}
}
impl<T> JoinHandle<T> {
/// Waits for the associated thread to finish.
#[track_caller]
pub fn join(self) -> std::thread::Result<T> {
self.notify.wait(location!());
self.result.lock().unwrap().take().unwrap()
}
/// Gets a handle to the underlying [`Thread`]
pub fn thread(&self) -> &Thread {
&self.thread
}
}
impl<T: fmt::Debug> fmt::Debug for JoinHandle<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("JoinHandle").finish()
}
}
fn _assert_traits() {
fn assert<T: Send + Sync>() {}
assert::<JoinHandle<()>>();
}
impl<T: 'static> LocalKey<T> {
/// Mock implementation of `std::thread::LocalKey::with`.
pub fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
self.try_with(f)
.expect("cannot access a (mock) TLS value during or after it is destroyed")
}
/// Mock implementation of `std::thread::LocalKey::try_with`.
pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
where
F: FnOnce(&T) -> R,
{
let value = match unsafe { self.get() } {
Some(v) => v?,
None => {
// Init the value out of the `rt::execution`
let value = (self.init)();
rt::execution(|execution| {
trace!("LocalKey::try_with");
execution.threads.local_init(self, value);
});
unsafe { self.get() }.expect("bug")?
}
};
Ok(f(value))
}
unsafe fn get(&'static self) -> Option<Result<&T, AccessError>> {
unsafe fn transmute_lt<'a, 'b, T>(t: &'a T) -> &'b T {
std::mem::transmute::<&'a T, &'b T>(t)
}
rt::execution(|execution| {
trace!("LocalKey::get");
let res = execution.threads.local(self)?;
let local = match res {
Ok(l) => l,
Err(e) => return Some(Err(e)),
};
// This is, sadly, necessary to allow nested `with` blocks to access
// different thread locals. The borrow on the thread-local needs to
// escape the lifetime of the borrow on `execution`, since
// `rt::execution` mutably borrows a RefCell, and borrowing it twice will
// cause a panic. This should be safe, as we know the function being
// passed the thread local will not outlive the thread on which
// it's executing, by construction --- it's just kind of unfortunate.
Some(Ok(transmute_lt(local)))
})
}
}
impl<T: 'static> fmt::Debug for LocalKey<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("LocalKey { .. }")
}
}