in src/thread.rs [54:103]
fn spawn_named<F, T>(f: F, name: Option<String>, stack_size: Option<usize>) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
// TODO Check if it's worth avoiding the call to `ExecutionState::config()` if we're going
// TODO to use an existing continuation from the pool.
let stack_size = stack_size.unwrap_or_else(|| ExecutionState::with(|s| s.config.stack_size));
let result = std::sync::Arc::new(std::sync::Mutex::new(None));
let task_id = {
let result = std::sync::Arc::clone(&result);
let f = move || {
let ret = f();
// Run thread-local destructors before publishing the result, because
// [`JoinHandle::join`] says join "waits for the associated thread to finish", but
// destructors must be run on the thread, so it can't be considered "finished" if the
// destructors haven't run yet.
// See `pop_local` for details on why this loop looks this slightly funky way.
while let Some(local) = ExecutionState::with(|state| state.current_mut().pop_local()) {
drop(local);
}
// Publish the result and unblock the waiter. We need to do this now, because once this
// closure completes, the Execution will consider this task Finished and invoke the
// scheduler.
*result.lock().unwrap() = Some(Ok(ret));
ExecutionState::with(|state| {
if let Some(waiter) = state.current_mut().take_waiter() {
state.get_mut(waiter).unblock();
}
});
};
ExecutionState::spawn_thread(f, stack_size, name.clone(), None)
};
thread::switch();
let thread = Thread {
id: ThreadId { task_id },
name,
};
JoinHandle {
task_id,
thread,
result,
}
}