use core::future::Future;
use std::io;
use std::sync::Arc;
use super::super::sched::Sched;
use super::super::shard::Shared;
use super::super::task::{self, Task};
use super::{Inner, RuntimeHandle, spawned};
use crate::sync::internal::{Ordering, fence};
use crate::task::JoinSet;
impl RuntimeHandle {
pub fn workers(&self) -> usize {
self.inner.workers.started()
}
pub fn spawn_on_worker<F>(&self, worker: usize, future: F) -> io::Result<Task<F::Output>>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
spawn_on_worker(&self.inner, worker, future)
}
pub fn spawn_local_on_worker<M, F>(&self, worker: usize, make: M) -> io::Result<Task<F::Output>>
where
M: FnOnce() -> F + Send + 'static,
F: Future + 'static,
F::Output: Send + 'static,
{
self.spawn_on_worker(worker, async move {
let task = crate::spawn_local(make());
task.await
})
}
pub fn on_each_worker<M, F>(&self, make: M) -> JoinSet<F::Output>
where
M: Fn() -> F + Send + Sync + 'static,
F: Future + 'static,
F::Output: Send + 'static,
{
let make = Arc::new(make);
let mut set = JoinSet::new();
for worker in 0..self.workers() {
let make = Arc::clone(&make);
if let Ok(task) = self.spawn_local_on_worker(worker, move || make()) {
set.insert(task);
}
}
set
}
}
fn worker_shard(inner: &Inner, worker: usize) -> io::Result<&Arc<Shared>> {
let started = inner.workers.started();
if worker >= started {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("worker {worker} does not exist: the runtime has started {started} workers"),
));
}
match inner.workers.get(worker) {
Some(shard) if shard.takes_tasks() => Ok(shard),
Some(_) => Err(stopped(worker)),
None => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("worker {worker} never started: its shard could not be made"),
)),
}
}
fn stopped(worker: usize) -> io::Error {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("worker {worker} has stopped and takes no new task"),
)
}
pub(super) fn spawn_on_worker<F>(
inner: &Inner,
worker: usize,
future: F,
) -> io::Result<Task<F::Output>>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let shard = worker_shard(inner, worker)?;
let hooks = inner.task_hooks.as_ref();
let (runnable, task) = task::spawn(
future,
Sched::counted(Arc::clone(shard), None, hooks.cloned()),
);
spawned(hooks.map(|hooks| &**hooks), &task);
fence(Ordering::SeqCst);
if !shard.takes_tasks() {
drop(runnable);
drop(task);
return Err(stopped(worker));
}
runnable.schedule();
Ok(task)
}