#[cfg(test)]
use super::registry::WorkerThread;
use super::{
ThreadPoolBuildError, ThreadPoolBuilder,
broadcast::{self, BroadcastContext},
latch::OnceLatch,
registry::Registry,
spawn,
};
use std::{
fmt,
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll, Wake, Waker},
};
#[cfg(test)]
mod future_test;
#[cfg(test)]
mod test;
pub struct ThreadPool {
registry: Arc<Registry>,
}
struct WorkerWake {
latch: OnceLatch,
registry: Arc<Registry>,
index: usize,
}
impl Wake for WorkerWake {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
unsafe { OnceLatch::set_and_tickle_one(&self.latch, &self.registry, self.index) };
}
}
impl ThreadPool {
pub(super) fn build(builder: ThreadPoolBuilder) -> Result<Self, ThreadPoolBuildError> {
let registry = Registry::new(builder)?;
Ok(Self { registry })
}
pub fn install<OP, R>(&self, op: OP) -> R
where
OP: FnOnce() -> R + Send,
R: Send,
{
self.registry.in_worker(|_, _| op())
}
pub fn broadcast<OP, R>(&self, op: OP) -> Vec<R>
where
OP: Fn(BroadcastContext<'_>) -> R + Sync,
R: Send,
{
unsafe { broadcast::broadcast_in(op, &self.registry) }
}
#[inline]
pub fn current_num_threads(&self) -> usize {
self.registry.num_threads()
}
#[inline]
pub fn current_thread_index(&self) -> Option<usize> {
let curr = self.registry.current_thread()?;
Some(curr.index())
}
pub(crate) fn block_on_worker<F: Future + ?Sized>(
&self,
mut future: Pin<&mut F>,
) -> Option<F::Output> {
let worker = self.registry.current_thread()?;
loop {
let wake = Arc::new(WorkerWake {
latch: OnceLatch::new(),
registry: Arc::clone(worker.registry()),
index: worker.index(),
});
let waker = Waker::from(Arc::clone(&wake));
match future.as_mut().poll(&mut Context::from_waker(&waker)) {
Poll::Ready(result) => return Some(result),
Poll::Pending => {
unsafe { worker.wait_until(&wake.latch) };
}
}
}
}
pub fn spawn<OP>(&self, op: OP)
where
OP: FnOnce() + Send + 'static,
{
unsafe { spawn::spawn_in(op, &self.registry) }
}
pub fn spawn_broadcast<OP>(&self, op: OP)
where
OP: Fn(BroadcastContext<'_>) + Send + Sync + 'static,
{
unsafe { broadcast::spawn_broadcast_in(op, &self.registry) }
}
pub fn yield_now(&self) -> Option<Yield> {
let curr = self.registry.current_thread()?;
Some(curr.yield_now())
}
}
impl Drop for ThreadPool {
fn drop(&mut self) {
self.registry.terminate();
}
}
impl fmt::Debug for ThreadPool {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ThreadPool")
.field("num_threads", &self.current_num_threads())
.field("id", &self.registry.id())
.finish()
}
}
#[cfg(test)]
#[inline]
pub(crate) fn current_thread_index() -> Option<usize> {
let curr = unsafe { WorkerThread::current().as_ref()? };
Some(curr.index())
}
#[cfg(test)]
pub(crate) fn yield_now() -> Option<Yield> {
let thread = unsafe { WorkerThread::current().as_ref()? };
Some(thread.yield_now())
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Yield {
Executed,
Idle,
}