mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-29 02:52:11 +00:00
109 lines
3 KiB
Rust
109 lines
3 KiB
Rust
//! A utility module for working with threads that automatically joins threads upon drop
|
|
//! and abstracts over operating system quality of service (`QoS`) APIs
|
|
//! through the concept of a “thread intent”.
|
|
//!
|
|
//! The intent of a thread is frozen at thread creation time,
|
|
//! i.e. there is no API to change the intent of a thread once it has been spawned.
|
|
//!
|
|
//! As a system, rust-analyzer should have the property that
|
|
//! old manual scheduling APIs are replaced entirely by `QoS`.
|
|
//! To maintain this invariant, we panic when it is clear that
|
|
//! old scheduling APIs have been used.
|
|
//!
|
|
//! Moreover, we also want to ensure that every thread has an intent set explicitly
|
|
//! to force a decision about its importance to the system.
|
|
//! Thus, [`ThreadIntent`] has no default value
|
|
//! and every entry point to creating a thread requires a [`ThreadIntent`] upfront.
|
|
|
|
use std::fmt;
|
|
|
|
mod intent;
|
|
mod pool;
|
|
|
|
pub use intent::ThreadIntent;
|
|
pub use pool::Pool;
|
|
|
|
/// # Panics
|
|
///
|
|
/// Panics if failed to spawn the thread.
|
|
pub fn spawn<F, T>(intent: ThreadIntent, name: String, f: F) -> JoinHandle<T>
|
|
where
|
|
F: (FnOnce() -> T) + Send + 'static,
|
|
T: Send + 'static,
|
|
{
|
|
Builder::new(intent, name).spawn(f).expect("failed to spawn thread")
|
|
}
|
|
|
|
pub struct Builder {
|
|
intent: ThreadIntent,
|
|
inner: jod_thread::Builder,
|
|
allow_leak: bool,
|
|
}
|
|
|
|
impl Builder {
|
|
#[must_use]
|
|
pub fn new(intent: ThreadIntent, name: impl Into<String>) -> Self {
|
|
Self { intent, inner: jod_thread::Builder::new().name(name.into()), allow_leak: false }
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn stack_size(self, size: usize) -> Self {
|
|
Self { inner: self.inner.stack_size(size), ..self }
|
|
}
|
|
|
|
/// Whether dropping should detach the thread
|
|
/// instead of joining it.
|
|
#[must_use]
|
|
pub fn allow_leak(self, allow_leak: bool) -> Self {
|
|
Self { allow_leak, ..self }
|
|
}
|
|
|
|
pub fn spawn<F, T>(self, f: F) -> std::io::Result<JoinHandle<T>>
|
|
where
|
|
F: (FnOnce() -> T) + Send + 'static,
|
|
T: Send + 'static,
|
|
{
|
|
let inner_handle = self.inner.spawn(move || {
|
|
self.intent.apply_to_current_thread();
|
|
f()
|
|
})?;
|
|
|
|
Ok(JoinHandle { inner: Some(inner_handle), allow_leak: self.allow_leak })
|
|
}
|
|
}
|
|
|
|
pub struct JoinHandle<T = ()> {
|
|
// `inner` is an `Option` so that we can
|
|
// take ownership of the contained `JoinHandle`.
|
|
inner: Option<jod_thread::JoinHandle<T>>,
|
|
allow_leak: bool,
|
|
}
|
|
|
|
impl<T> JoinHandle<T> {
|
|
/// # Panics
|
|
///
|
|
/// Panics if there is no thread to join.
|
|
#[must_use]
|
|
pub fn join(mut self) -> T {
|
|
self.inner.take().unwrap().join()
|
|
}
|
|
}
|
|
|
|
impl<T> Drop for JoinHandle<T> {
|
|
fn drop(&mut self) {
|
|
if !self.allow_leak {
|
|
return;
|
|
}
|
|
|
|
if let Some(join_handle) = self.inner.take() {
|
|
join_handle.detach();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[expect(clippy::min_ident_chars, reason = "trait impl")]
|
|
impl<T> fmt::Debug for JoinHandle<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.pad("JoinHandle { .. }")
|
|
}
|
|
}
|