upstream stdx changes

This commit is contained in:
BenjaminBrienen 2025-04-04 18:37:21 +02:00
parent 2261e4e892
commit 428ee50540
11 changed files with 131 additions and 97 deletions

4
Cargo.lock generated
View file

@ -1083,9 +1083,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jod-thread"
version = "0.1.2"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
checksum = "a037eddb7d28de1d0fc42411f501b53b75838d313908078d6698d064f3029b24"
[[package]]
name = "kqueue"

View file

@ -13,7 +13,7 @@ rust-version.workspace = true
[dependencies]
backtrace = { version = "0.3.74", optional = true }
jod-thread = "0.1.2"
jod-thread = "1.0.0"
crossbeam-channel.workspace = true
itertools.workspace = true
tracing.workspace = true

View file

@ -1,4 +1,5 @@
//! This file is a port of only the necessary features from <https://github.com/chris-morgan/anymap> version 1.0.0-beta.2 for use within rust-analyzer.
//!
//! Copyright © 20142022 Chris Morgan.
//! COPYING: <https://github.com/chris-morgan/anymap/blob/master/COPYING>
//! Note that the license is changed from Blue Oak Model 1.0.0 or MIT or Apache-2.0 to MIT OR Apache-2.0
@ -20,14 +21,14 @@
use core::hash::Hasher;
/// A hasher designed to eke a little more speed out, given `TypeId`s known characteristics.
/// A hasher designed to eke a little more speed out, given `TypeId`'s known characteristics.
///
/// Specifically, this is a no-op hasher that expects to be fed a u64s worth of
/// Specifically, this is a no-op hasher that expects to be fed a u64's worth of
/// randomly-distributed bits. It works well for `TypeId` (eliminating start-up time, so that my
/// get_missing benchmark is ~30ns rather than ~900ns, and being a good deal faster after that, so
/// that my insert_and_get_on_260_types benchmark is ~12μs instead of ~21.5μs), but will
/// `get_missing` benchmark is ~30ns rather than ~900ns, and being a good deal faster after that, so
/// that my `insert_and_get_on_260_types` benchmark is ~12μs instead of ~21.5μs), but will
/// panic in debug mode and always emit zeros in release mode for any other sorts of inputs, so
/// yeah, dont use it! 😀
/// yeah, don't use it! 😀
#[derive(Default)]
pub struct TypeIdHasher {
value: u64,
@ -36,9 +37,9 @@ pub struct TypeIdHasher {
impl Hasher for TypeIdHasher {
#[inline]
fn write(&mut self, bytes: &[u8]) {
// This expects to receive exactly one 64-bit value, and theres no realistic chance of
// that changing, but I dont want to depend on something that isnt expressly part of the
// contract for safety. But Im OK with release builds putting everything in one bucket
// This expects to receive exactly one 64-bit value, and there's no realistic chance of
// that changing, but I don't want to depend on something that isn't expressly part of the
// contract for safety. But I'm OK with release builds putting everything in one bucket
// if it *did* change (and debug builds panicking).
debug_assert_eq!(bytes.len(), 8);
let _ = bytes.try_into().map(|array| self.value = u64::from_ne_bytes(array));
@ -59,7 +60,7 @@ use ::std::collections::hash_map;
/// Raw access to the underlying `HashMap`.
///
/// This alias is provided for convenience because of the ugly third generic parameter.
#[allow(clippy::disallowed_types)] // Uses a custom hasher
#[expect(clippy::disallowed_types, reason = "Uses a custom hasher")]
pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeIdHasher>>;
/// A collection containing zero or one values for any given type and allowing convenient,
@ -73,19 +74,20 @@ pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeId
///
/// Cumulatively, there are thus six forms of map:
///
/// - <code>[Map]&lt;dyn [core::any::Any]&gt;</code>,
/// - `[Map]<dyn [core::any::Any]>`,
/// also spelled [`AnyMap`] for convenience.
/// - <code>[Map]&lt;dyn [core::any::Any] + Send&gt;</code>
/// - <code>[Map]&lt;dyn [core::any::Any] + Send + Sync&gt;</code>
/// - `[Map]<dyn [core::any::Any] + Send>`
/// - `[Map]<dyn [core::any::Any] + Send + Sync>`
///
/// ## Example
///
/// (Here using the [`AnyMap`] convenience alias; the first line could use
/// <code>[anymap::Map][Map]::&lt;[core::any::Any]&gt;::new()</code> instead if desired.)
/// (Here, the [`AnyMap`] convenience alias is used;
/// the first line could use `[anymap::Map][Map]::<[core::any::Any]>::new()`
/// instead if desired.)
///
/// ```
/// # use stdx::anymap;
#[doc = "let mut data = anymap::AnyMap::new();"]
/// let mut data = anymap::AnyMap::new();
/// assert_eq!(data.get(), None::<&i32>);
/// ```
///
@ -95,11 +97,11 @@ pub struct Map<A: ?Sized + Downcast = dyn Any> {
raw: RawMap<A>,
}
/// The most common type of `Map`: just using `Any`; <code>[Map]&lt;dyn [Any]&gt;</code>.
/// The most common type of `Map`: just using `Any`; `[Map]<dyn [Any]>`.
///
/// Why is this a separate type alias rather than a default value for `Map<A>`?
/// `Map::new()` doesnt seem to be happy to infer that it should go with the default
/// value. Its a bit sad, really. Ah well, I guess this approach will do.
/// `Map::new()` doesn't seem to be happy to infer that it should go with the default
/// value. It's a bit sad, really. Ah well, I guess this approach will do.
pub type AnyMap = Map<dyn Any>;
impl<A: ?Sized + Downcast> Default for Map<A> {
@ -113,6 +115,7 @@ impl<A: ?Sized + Downcast> Map<A> {
/// Returns a reference to the value stored in the collection for the type `T`,
/// if it exists.
#[inline]
#[must_use]
pub fn get<T: IntoBox<A>>(&self) -> Option<&T> {
self.raw.get(&TypeId::of::<T>()).map(|any| unsafe { any.downcast_ref_unchecked::<T>() })
}
@ -132,30 +135,30 @@ impl<A: ?Sized + Downcast> Map<A> {
}
/// A view into a single occupied location in an `Map`.
pub struct OccupiedEntry<'a, A: ?Sized + Downcast, V: 'a> {
inner: hash_map::OccupiedEntry<'a, TypeId, Box<A>>,
pub struct OccupiedEntry<'map, A: ?Sized + Downcast, V: 'map> {
inner: hash_map::OccupiedEntry<'map, TypeId, Box<A>>,
type_: PhantomData<V>,
}
/// A view into a single empty location in an `Map`.
pub struct VacantEntry<'a, A: ?Sized + Downcast, V: 'a> {
inner: hash_map::VacantEntry<'a, TypeId, Box<A>>,
pub struct VacantEntry<'map, A: ?Sized + Downcast, V: 'map> {
inner: hash_map::VacantEntry<'map, TypeId, Box<A>>,
type_: PhantomData<V>,
}
/// A view into a single location in an `Map`, which may be vacant or occupied.
pub enum Entry<'a, A: ?Sized + Downcast, V> {
pub enum Entry<'map, A: ?Sized + Downcast, V> {
/// An occupied Entry
Occupied(OccupiedEntry<'a, A, V>),
Occupied(OccupiedEntry<'map, A, V>),
/// A vacant Entry
Vacant(VacantEntry<'a, A, V>),
Vacant(VacantEntry<'map, A, V>),
}
impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> Entry<'a, A, V> {
impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> Entry<'map, A, V> {
/// Ensures a value is in the entry by inserting the result of the default function if
/// empty, and returns a mutable reference to the value in the entry.
#[inline]
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'map mut V {
match self {
Entry::Occupied(inner) => inner.into_mut(),
Entry::Vacant(inner) => inner.insert(default()),
@ -163,20 +166,21 @@ impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> Entry<'a, A, V> {
}
}
impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> OccupiedEntry<'a, A, V> {
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> OccupiedEntry<'map, A, V> {
/// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
/// with a lifetime bound to the collection itself
#[inline]
pub fn into_mut(self) -> &'a mut V {
#[must_use]
pub fn into_mut(self) -> &'map mut V {
unsafe { self.inner.into_mut().downcast_mut_unchecked() }
}
}
impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> VacantEntry<'a, A, V> {
/// Sets the value of the entry with the VacantEntry's key,
impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> VacantEntry<'map, A, V> {
/// Sets the value of the entry with the `VacantEntry`'s key,
/// and returns a mutable reference to it
#[inline]
pub fn insert(self, value: V) -> &'a mut V {
pub fn insert(self, value: V) -> &'map mut V {
unsafe { self.inner.insert(value.into_box()).downcast_mut_unchecked() }
}
}
@ -201,14 +205,13 @@ mod tests {
#[test]
fn type_id_hasher() {
use core::any::TypeId;
use core::hash::Hash;
use core::hash::Hash as _;
fn verify_hashing_with(type_id: TypeId) {
let mut hasher = TypeIdHasher::default();
type_id.hash(&mut hasher);
// SAFETY: u64 is valid for all bit patterns.
let _ = hasher.finish();
_ = hasher.finish();
}
// Pick a variety of types, just to demonstrate its all sane. Normal, zero-sized, unsized, &c.
// Pick a variety of types, just to demonstrate it's all sane. Normal, zero-sized, unsized, &c.
verify_hashing_with(TypeId::of::<usize>());
verify_hashing_with(TypeId::of::<()>());
verify_hashing_with(TypeId::of::<str>());
@ -220,34 +223,34 @@ mod tests {
/// Methods for downcasting from an `Any`-like trait object.
///
/// This should only be implemented on trait objects for subtraits of `Any`, though you can
/// implement it for other types and itll work fine, so long as your implementation is correct.
/// implement it for other types and it'll work fine, so long as your implementation is correct.
pub trait Downcast {
/// Gets the `TypeId` of `self`.
fn type_id(&self) -> TypeId;
// Note the bound through these downcast methods is 'static, rather than the inexpressible
// concept of Self-but-as-a-trait (where Self is `dyn Trait`). This is sufficient, exceeding
// TypeIds requirements. Sure, you *can* do CloneAny.downcast_unchecked::<NotClone>() and the
// type system wont protect you, but that doesnt introduce any unsafety: the method is
// TypeId's requirements. Sure, you *can* do CloneAny.downcast_unchecked::<NotClone>() and the
// type system won't protect you, but that doesn't introduce any unsafety: the method is
// already unsafe because you can specify the wrong type, and if this were exposing safe
// downcasting, CloneAny.downcast::<NotClone>() would just return an error, which is just as
// correct.
//
// Now in theory we could also add T: ?Sized, but that doesnt play nicely with the common
// implementation, so Im doing without it.
// Now in theory we could also add T: ?Sized, but that doesn't play nicely with the common
// implementation, so I'm doing without it.
/// Downcast from `&Any` to `&T`, without checking the type matches.
///
/// # Safety
///
/// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*.
/// The caller must ensure that `T` matches the trait object, on pain of *undefined behavior*.
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T;
/// Downcast from `&mut Any` to `&mut T`, without checking the type matches.
///
/// # Safety
///
/// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*.
/// The caller must ensure that `T` matches the trait object, on pain of *undefined behavior*.
unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T;
}
@ -267,12 +270,12 @@ macro_rules! implement {
#[inline]
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
unsafe { &*(self as *const Self as *const T) }
unsafe { &*std::ptr::from_ref::<Self>(self).cast::<T>() }
}
#[inline]
unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T {
unsafe { &mut *(self as *mut Self as *mut T) }
unsafe { &mut *std::ptr::from_mut::<Self>(self).cast::<T>() }
}
}

View file

@ -17,7 +17,7 @@ pub mod thread;
pub use itertools;
#[inline(always)]
pub fn is_ci() -> bool {
pub const fn is_ci() -> bool {
option_env!("CI").is_some()
}
@ -26,14 +26,14 @@ pub fn hash_once<Hasher: std::hash::Hasher + Default>(thing: impl std::hash::Has
}
#[must_use]
#[allow(clippy::print_stderr)]
#[expect(clippy::print_stderr, reason = "only visible to developers")]
pub fn timeit(label: &'static str) -> impl Drop {
let start = Instant::now();
defer(move || eprintln!("{}: {:.2?}", label, start.elapsed()))
defer(move || eprintln!("{}: {:.2}", label, start.elapsed().as_nanos()))
}
/// Prints backtrace to stderr, useful for debugging.
#[allow(clippy::print_stderr)]
#[expect(clippy::print_stderr, reason = "only visible to developers")]
pub fn print_backtrace() {
#[cfg(feature = "backtrace")]
eprintln!("{:?}", backtrace::Backtrace::new());
@ -126,6 +126,7 @@ where
}
// Taken from rustc.
#[must_use]
pub fn to_camel_case(ident: &str) -> String {
ident
.trim_matches('_')
@ -156,7 +157,7 @@ pub fn to_camel_case(ident: &str) -> String {
camel_cased_component
})
.fold((String::new(), None), |(acc, prev): (_, Option<String>), next| {
.fold((String::new(), None), |(mut acc, prev): (_, Option<String>), next| {
// separate two components with an underscore if their boundary cannot
// be distinguished using an uppercase/lowercase case distinction
let join = prev
@ -166,16 +167,20 @@ pub fn to_camel_case(ident: &str) -> String {
Some(!char_has_case(l) && !char_has_case(f))
})
.unwrap_or(false);
(acc + if join { "_" } else { "" } + &next, Some(next))
acc.push_str(if join { "_" } else { "" });
acc.push_str(&next);
(acc, Some(next))
})
.0
}
// Taken from rustc.
pub fn char_has_case(c: char) -> bool {
#[must_use]
pub const fn char_has_case(c: char) -> bool {
c.is_lowercase() || c.is_uppercase()
}
#[must_use]
pub fn is_upper_snake_case(s: &str) -> bool {
s.chars().all(|c| c.is_uppercase() || c == '_' || c.is_numeric())
}
@ -188,6 +193,7 @@ pub fn replace(buf: &mut String, from: char, to: &str) {
*buf = buf.replace(from, to);
}
#[must_use]
pub fn trim_indent(mut text: &str) -> String {
if text.starts_with('\n') {
text = &text[1..];
@ -249,8 +255,8 @@ impl ops::DerefMut for JodChild {
impl Drop for JodChild {
fn drop(&mut self) {
let _ = self.0.kill();
let _ = self.0.wait();
_ = self.0.kill();
_ = self.0.wait();
}
}
@ -259,12 +265,11 @@ impl JodChild {
command.spawn().map(Self)
}
#[must_use]
#[cfg(not(target_arch = "wasm32"))]
pub fn into_inner(self) -> std::process::Child {
if cfg!(target_arch = "wasm32") {
panic!("no processes on wasm");
}
// SAFETY: repr transparent, except on WASM
unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
unsafe { std::mem::transmute::<Self, std::process::Child>(self) }
}
}

View file

@ -8,8 +8,8 @@ pub struct NonEmptyVec<T> {
impl<T> NonEmptyVec<T> {
#[inline]
pub fn new(first: T) -> Self {
NonEmptyVec { first, rest: Vec::new() }
pub const fn new(first: T) -> Self {
Self { first, rest: Vec::new() }
}
#[inline]
@ -24,7 +24,7 @@ impl<T> NonEmptyVec<T> {
#[inline]
pub fn push(&mut self, value: T) {
self.rest.push(value)
self.rest.push(value);
}
#[inline]

View file

@ -16,7 +16,7 @@ impl Drop for PanicContext {
}
pub fn enter(frame: String) -> PanicContext {
#[allow(clippy::print_stderr)]
#[expect(clippy::print_stderr, reason = "already panicking anyway")]
fn set_hook() {
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {

View file

@ -54,6 +54,9 @@ pub fn streaming_output(
Ok((stdout, stderr))
}
/// # Panics
///
/// Panics if `cmd` is not configured to have `stdout` and `stderr` as `piped`.
pub fn spawn_with_streaming_output(
mut cmd: Command,
on_stdout_line: &mut dyn FnMut(&str),

View file

@ -1,8 +1,7 @@
//! We don't use `rand`, as that's too many things for us.
//! We don't use `rand` because that is too many things for us.
//!
//! We currently use oorandom instead, but it's missing these two utilities.
//! Perhaps we should switch to `fastrand`, or our own small PRNG, it's not like
//! we need anything more complicated than xor-shift.
//! `oorandom` is used instead, but it's missing these two utilities.
//! Switching to `fastrand` or our own small PRNG may be good because only xor-shift is needed.
pub fn shuffle<T>(slice: &mut [T], mut rand_index: impl FnMut(usize) -> usize) {
let mut remaining = slice.len() - 1;

View file

@ -1,12 +1,12 @@
//! A utility module for working with threads that automatically joins threads upon drop
//! and abstracts over operating system quality of service (QoS) APIs
//! 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.
//! 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.
//!
@ -23,10 +23,12 @@ 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, f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
F: (FnOnce() -> T) + Send + 'static,
T: Send + 'static,
{
Builder::new(intent).spawn(f).expect("failed to spawn thread")
@ -39,26 +41,29 @@ pub struct Builder {
}
impl Builder {
pub fn new(intent: ThreadIntent) -> Builder {
Builder { intent, inner: jod_thread::Builder::new(), allow_leak: false }
#[must_use]
pub fn new(intent: ThreadIntent) -> Self {
Self { intent, inner: jod_thread::Builder::new(), allow_leak: false }
}
pub fn name(self, name: String) -> Builder {
Builder { inner: self.inner.name(name), ..self }
#[must_use]
pub fn name(self, name: String) -> Self {
Self { inner: self.inner.name(name), ..self }
}
pub fn stack_size(self, size: usize) -> Builder {
Builder { inner: self.inner.stack_size(size), ..self }
#[must_use]
pub fn stack_size(self, size: usize) -> Self {
Self { inner: self.inner.stack_size(size), ..self }
}
pub fn allow_leak(self, b: bool) -> Builder {
Builder { allow_leak: b, ..self }
#[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,
F: Send + 'static,
F: (FnOnce() -> T) + Send + 'static,
T: Send + 'static,
{
let inner_handle = self.inner.spawn(move || {
@ -78,6 +83,10 @@ pub struct JoinHandle<T = ()> {
}
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()
}
@ -95,6 +104,7 @@ impl<T> Drop for JoinHandle<T> {
}
}
#[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 { .. }")

View file

@ -1,9 +1,9 @@
//! An opaque façade around platform-specific QoS APIs.
//! An opaque façade around platform-specific `QoS` APIs.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
// Please maintain order from least to most priority for the derived `Ord` impl.
pub enum ThreadIntent {
/// Any thread which does work that isnt in the critical path of the user typing
/// Any thread which does work that isn't in the critical path of the user typing
/// (e.g. processing Go To Definition).
Worker,
@ -34,6 +34,7 @@ use imp::QoSClass;
const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE;
#[expect(clippy::semicolon_if_nothing_returned, reason = "thin wrapper")]
fn set_current_thread_qos_class(class: QoSClass) {
imp::set_current_thread_qos_class(class)
}
@ -63,7 +64,7 @@ mod imp {
///
/// * **You do not care about how long it takes for work to finish.**
/// * **You do not care about work being deferred temporarily.**
/// (e.g. if the devices battery is in a critical state)
/// (e.g. if the device's battery is in a critical state)
///
/// Examples:
///
@ -84,7 +85,7 @@ mod imp {
/// All other work is prioritized over background tasks.
Background,
/// TLDR: tasks that dont block using your app
/// TLDR: tasks that don't block using your app
///
/// Contract:
///
@ -110,7 +111,7 @@ mod imp {
/// for tasks using this class.
///
/// This QoS class provides a balance between
/// performance, responsiveness and efficiency.
/// performance, responsiveness, and efficiency.
Utility,
/// TLDR: tasks that block using your app
@ -126,10 +127,10 @@ mod imp {
/// * in a video editor:
/// opening a saved project
/// * in a browser:
/// loading a list of the users bookmarks and top sites
/// loading a list of the user's bookmarks and top sites
/// when a new tab is created
/// * in a collaborative word processor:
/// running a search on the documents content
/// running a search on the document's content
///
/// Use this QoS class for tasks which were initiated by the user
/// and block the usage of your app while they are in progress.
@ -208,7 +209,7 @@ mod imp {
}
_ => {
// `pthread_set_qos_class_self_np`s documentation
// `pthread_set_qos_class_self_np`'s documentation
// does not mention any other errors.
unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}")
}
@ -223,7 +224,7 @@ mod imp {
};
if code != 0 {
// `pthread_get_qos_class_np`s documentation states that
// `pthread_get_qos_class_np`'s documentation states that
// an error value is placed into errno if the return code is not zero.
// However, it never states what errors are possible.
// Inspecting the source[0] shows that, as of this writing, it always returns zero.

View file

@ -38,7 +38,11 @@ struct Job {
}
impl Pool {
pub fn new(threads: usize) -> Pool {
/// # Panics
///
/// Panics if job panics
#[must_use]
pub fn new(threads: usize) -> Self {
const STACK_SIZE: usize = 8 * 1024 * 1024;
const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker;
@ -63,7 +67,7 @@ impl Pool {
}
extant_tasks.fetch_add(1, Ordering::SeqCst);
// discard the panic, we should've logged the backtrace already
_ = panic::catch_unwind(job.f);
drop(panic::catch_unwind(job.f));
extant_tasks.fetch_sub(1, Ordering::SeqCst);
}
}
@ -73,9 +77,12 @@ impl Pool {
handles.push(handle);
}
Pool { _handles: handles.into_boxed_slice(), extant_tasks, job_sender }
Self { _handles: handles.into_boxed_slice(), extant_tasks, job_sender }
}
/// # Panics
///
/// Panics if job panics
pub fn spawn<F>(&self, intent: ThreadIntent, f: F)
where
F: FnOnce() + Send + UnwindSafe + 'static,
@ -84,14 +91,20 @@ impl Pool {
if cfg!(debug_assertions) {
intent.assert_is_used_on_current_thread();
}
f()
f();
});
let job = Job { requested_intent: intent, f };
self.job_sender.send(job).unwrap();
}
#[must_use]
pub fn len(&self) -> usize {
self.extant_tasks.load(Ordering::SeqCst)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}