fn spawn_named()

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,
    }
}