use super::task::{AsyncTask, ParTask, Task};
use crate::{
cb_deque::{Injector, Steal, Stealer, Worker},
ecs::{
cmd::CommandObject,
worker::{Message, WorkerId},
},
utils::ds::{Signal, UnsafeFuture},
};
use crossbeam_channel::{SendError, Sender, TryRecvError};
use my_utils::ds::SignalSlot;
use std::{
cell::Cell,
sync::{
atomic::{AtomicBool, AtomicU32, Ordering},
Arc, Mutex,
},
thread::{self, Thread},
};
thread_local! {
pub(crate) static WORKER_ID_GEN: Cell<u32> = const { Cell::new(0) };
}
#[derive(Debug)]
pub(crate) struct SubComm {
injector: Arc<Injector<Task>>,
local: Worker<Task>,
siblings: Arc<[Stealer<Task>]>,
futures: Arc<[Injector<Task>]>,
tx_msg: ParkingSender<Message>,
tx_cmd: CommandSender,
signal: Arc<GroupSignal>,
wid: WorkerId,
}
impl SubComm {
pub(super) fn with_len(
group_index: u16,
injector: &Arc<Injector<Task>>,
signal: &Arc<GroupSignal>,
tx_msg: &ParkingSender<Message>,
tx_cmd: &CommandSender,
len: usize,
) -> Vec<Self> {
let (locals, siblings): (Vec<_>, Vec<_>) = (0..len)
.map(|_| {
let local = Worker::<Task>::new_lifo();
let sibling = local.stealer();
(local, sibling)
})
.unzip();
let siblings: Arc<[Stealer<Task>]> = siblings.into();
let asyncs: Arc<[Injector<Task>]> = (0..len).map(|_| Injector::new()).collect();
locals
.into_iter()
.enumerate()
.map(|(worker_index, local)| {
let id = WORKER_ID_GEN.with(Cell::get);
WORKER_ID_GEN.with(|gen| gen.set(id + 1));
Self {
injector: Arc::clone(injector),
local,
siblings: Arc::clone(&siblings),
futures: Arc::clone(&asyncs),
tx_msg: tx_msg.clone(),
tx_cmd: tx_cmd.clone(),
wid: WorkerId::new(id, group_index, worker_index as u16),
signal: Arc::clone(signal),
}
})
.collect()
}
pub(crate) fn signal(&self) -> &GroupSignal {
&self.signal
}
pub(crate) fn worker_id(&self) -> WorkerId {
let wid = self.maybe_uninit_worker_id();
#[cfg(debug_assertions)]
{
use crate::ecs::sched::ctrl::WORKER_ID;
assert_eq!(wid, WORKER_ID.with(Cell::get));
}
wid
}
pub(super) fn maybe_uninit_worker_id(&self) -> WorkerId {
self.wid
}
pub(super) fn num_siblings(&self) -> usize {
self.siblings.len()
}
pub(super) fn set_signal(&mut self, signal: Arc<GroupSignal>) {
self.signal = signal;
}
pub(super) fn wait(&self) {
self.signal.sub().wait(self.wid.worker_index() as usize);
}
pub(super) fn wake_self(&self) {
self.signal
.sub()
.notify_try(self.wid.worker_index() as usize);
}
pub(crate) fn send_message(&self, msg: Message) {
self.tx_msg.send(msg).unwrap();
}
pub(crate) fn send_command_or_cancel(&self, cmd: CommandObject) {
self.tx_cmd.send_or_cancel(cmd);
}
pub(super) fn pop(&self) -> Steal<Task> {
if let Some(task) = self.pop_local() {
Steal::Success(task)
} else {
self.pop_future()
}
}
pub(super) fn pop_local(&self) -> Option<Task> {
self.local.pop()
}
pub(super) fn pop_future(&self) -> Steal<Task> {
loop {
let steal = self.futures[self.wid.worker_index() as usize].steal();
match &steal {
Steal::Retry => {}
_ => return steal,
}
}
}
pub(super) fn push_parallel_task(&self, task: ParTask) {
self.local.push(Task::Parallel(task));
}
pub(crate) fn push_future_task(&self, handle: UnsafeFuture) {
let task = Task::Async(AsyncTask(handle));
self.futures[self.wid.worker_index() as usize].push(task);
}
pub(crate) fn is_local_empty(&self) -> bool {
self.local.is_empty()
}
pub(super) fn search(&self) -> Steal<Task> {
self.search_injector()
.or_else(|| self.search_sibling_locals())
.or_else(|| self.search_futures())
}
pub(super) fn search_injector(&self) -> Steal<Task> {
loop {
let steal = self.injector.steal_batch_and_pop(&self.local);
match &steal {
Steal::Success(_task) => {
if !self.local.is_empty() {
self.signal.sub().notify_one();
}
return steal;
}
Steal::Empty => break,
Steal::Retry => {}
}
}
Steal::Empty
}
pub(super) fn search_sibling_locals(&self) -> Steal<Task> {
for sibling in self
.siblings
.iter()
.cycle()
.skip(self.wid.worker_index() as usize + 1)
.take(self.siblings.len() - 1)
{
loop {
let steal = sibling.steal_batch_and_pop(&self.local);
match &steal {
Steal::Success(_task) => {
if !(self.local.is_empty() && sibling.is_empty()) {
self.signal.sub().notify_one();
}
return steal;
}
Steal::Empty => break,
Steal::Retry => {}
}
}
}
Steal::Empty
}
pub(super) fn search_futures(&self) -> Steal<Task> {
for sibling in self
.futures
.iter()
.cycle()
.skip(self.wid.worker_index() as usize)
.take(self.futures.len())
{
loop {
let steal = sibling.steal_batch_and_pop(&self.local);
match &steal {
Steal::Success(_task) => {
if !(self.local.is_empty() && sibling.is_empty()) {
self.signal.sub().notify_one();
}
return steal;
}
Steal::Empty => break,
Steal::Retry => {}
}
}
}
Steal::Empty
}
}
#[derive(Debug)]
pub(crate) struct GroupSignal {
main: Thread,
sub: Signal,
is_abort: AtomicBool,
open_cnt: AtomicU32,
work_cnt: AtomicU32,
fut_cnt: AtomicU32,
}
impl GroupSignal {
pub(super) fn new(signal_slots: Vec<SignalSlot>) -> Self {
Self {
main: thread::current(),
sub: Signal::new(signal_slots),
is_abort: AtomicBool::new(false),
open_cnt: AtomicU32::new(0),
work_cnt: AtomicU32::new(0),
fut_cnt: AtomicU32::new(0),
}
}
pub(crate) fn sub(&self) -> &Signal {
&self.sub
}
pub(crate) fn is_abort(&self) -> bool {
self.is_abort.load(Ordering::Relaxed)
}
pub(crate) fn set_abort(&self, is_abort: bool) {
self.is_abort.store(is_abort, Ordering::Relaxed);
}
pub(crate) fn open_count(&self) -> u32 {
self.open_cnt.load(Ordering::Acquire)
}
pub(crate) fn wait_open_count(&self, target: u32) {
while self.open_cnt.load(Ordering::Acquire) != target {
thread::park();
}
}
pub(crate) fn add_open_count(&self, value: u32) -> u32 {
let old = self.open_cnt.fetch_add(value, Ordering::Release);
self.main.unpark();
old.wrapping_add(value)
}
pub(crate) fn sub_open_count(&self, value: u32) -> u32 {
let old = self.open_cnt.fetch_sub(value, Ordering::Release);
self.main.unpark();
old.wrapping_sub(value)
}
pub(crate) fn work_count(&self) -> u32 {
self.work_cnt.load(Ordering::Acquire)
}
pub(crate) fn add_work_count(&self, value: u32) -> u32 {
let old = self.work_cnt.fetch_add(value, Ordering::Release);
old.wrapping_add(value)
}
pub(crate) fn sub_work_count(&self, value: u32) -> u32 {
let old = self.work_cnt.fetch_sub(value, Ordering::Release);
old.wrapping_sub(value)
}
pub(crate) fn future_count(&self) -> u32 {
self.fut_cnt.load(Ordering::Acquire)
}
pub(crate) fn add_future_count(&self, value: u32) -> u32 {
let old = self.fut_cnt.fetch_add(value, Ordering::Release);
old.wrapping_add(value)
}
pub(crate) fn sub_future_count(&self, value: u32) -> u32 {
let old = self.fut_cnt.fetch_sub(value, Ordering::Release);
old.wrapping_sub(value)
}
}
#[derive(Debug, Clone)]
pub(crate) struct CommandSender {
inner: ParkingSender<CommandObject>,
open: Arc<Mutex<bool>>,
}
impl CommandSender {
pub(crate) fn send_or_cancel(&self, cmd: CommandObject) {
if let Err(SendError(cmd)) = self.send(cmd) {
cmd.cancel();
}
}
fn send(&self, cmd: CommandObject) -> Result<(), SendError<CommandObject>> {
let guard = self.open.lock().unwrap();
if *guard {
self.inner.send(cmd)
} else {
Err(SendError(cmd))
}
}
}
#[derive(Debug)]
pub(crate) struct CommandReceiver {
inner: ParkingReceiver<CommandObject>,
open: Arc<Mutex<bool>>,
}
impl CommandReceiver {
pub(crate) fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub(crate) fn try_recv(&self) -> Result<CommandObject, TryRecvError> {
self.inner.try_recv()
}
pub(crate) fn close(&self) {
*self.open.lock().unwrap() = false;
}
}
pub(crate) fn command_channel(th: Thread) -> (CommandSender, CommandReceiver) {
let (tx, rx) = parking_channel(th);
let open = Arc::new(Mutex::new(true));
let c_open = Arc::clone(&open);
(
CommandSender { inner: tx, open },
CommandReceiver {
inner: rx,
open: c_open,
},
)
}
pub(super) fn parking_channel<T>(th: Thread) -> (ParkingSender<T>, ParkingReceiver<T>) {
let (tx, rx) = crossbeam_channel::unbounded();
(ParkingSender::new(tx, th.clone()), ParkingReceiver::new(rx))
}
#[derive(Debug)]
pub(crate) struct ParkingSender<T> {
tx: Sender<T>,
th: Thread,
}
impl<T> ParkingSender<T> {
pub(crate) const fn new(tx: Sender<T>, th: Thread) -> Self {
Self { tx, th }
}
pub(crate) fn send(&self, t: T) -> Result<(), SendError<T>> {
let res = self.tx.send(t);
self.th.unpark();
res
}
}
impl<T> Clone for ParkingSender<T> {
fn clone(&self) -> Self {
Self {
tx: self.tx.clone(),
th: self.th.clone(),
}
}
}
#[cfg(not(debug_assertions))]
pub(crate) use parking_receiver_release::*;
#[cfg(debug_assertions)]
pub(crate) use parking_receiver_debug::*;
#[cfg(not(debug_assertions))]
mod parking_receiver_release {
use crossbeam_channel::{Receiver, RecvTimeoutError, TryRecvError};
use std::{cell::Cell, fmt, thread, time::Duration};
pub(crate) struct ParkingReceiver<T> {
rx: Receiver<T>,
next: Cell<Option<T>>,
}
impl<T> std::fmt::Debug for ParkingReceiver<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ParkingReceiver")
.field("rx", &self.rx)
.finish_non_exhaustive()
}
}
impl<T> ParkingReceiver<T> {
pub(crate) const fn new(rx: Receiver<T>) -> Self {
Self {
rx,
next: Cell::new(None),
}
}
pub(crate) fn is_empty(&self) -> bool {
if let Ok(value) = self.try_recv() {
self.next.set(Some(value));
false
} else {
true
}
}
pub(crate) fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
if let Ok(value) = self.try_recv() {
Ok(value)
} else {
thread::park_timeout(timeout);
self.rx.try_recv().map_err(|err| match err {
TryRecvError::Empty => RecvTimeoutError::Timeout,
TryRecvError::Disconnected => RecvTimeoutError::Disconnected,
})
}
}
pub(crate) fn wait_timeout(&self, timeout: Duration) {
if let Ok(value) = self.try_recv() {
self.next.set(Some(value));
} else {
thread::park_timeout(timeout);
}
}
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
if let Some(value) = self.next.take() {
Ok(value)
} else {
self.rx.try_recv()
}
}
}
}
#[cfg(debug_assertions)]
mod parking_receiver_debug {
use crossbeam_channel::{Receiver, RecvTimeoutError, TryRecvError};
use std::{
cell::{RefCell, RefMut},
collections::VecDeque,
thread,
time::Duration,
};
#[derive(Debug)]
pub(crate) struct ParkingReceiver<T> {
rx: Receiver<T>,
buf: RefCell<VecDeque<T>>,
}
impl<T> ParkingReceiver<T> {
pub(crate) fn new(rx: Receiver<T>) -> Self {
Self {
rx,
buf: RefCell::new(VecDeque::new()),
}
}
pub(crate) fn is_empty(&self) -> bool {
if let Ok(value) = self.try_recv() {
self.buf.borrow_mut().push_front(value);
false
} else {
true
}
}
pub(crate) fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
if let Ok(value) = self.try_recv() {
Ok(value)
} else {
thread::park_timeout(timeout);
self.rx.try_recv().map_err(|err| match err {
TryRecvError::Empty => RecvTimeoutError::Timeout,
TryRecvError::Disconnected => RecvTimeoutError::Disconnected,
})
}
}
pub(crate) fn wait_timeout(&self, timeout: Duration) {
if let Ok(value) = self.try_recv() {
self.buf.borrow_mut().push_front(value);
} else {
thread::park_timeout(timeout);
}
}
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
if let Some(value) = self.buf.borrow_mut().pop_front() {
Ok(value)
} else {
self.rx.try_recv()
}
}
#[allow(unused)]
pub(crate) fn buffer(&self) -> RefMut<'_, VecDeque<T>> {
let mut buf = self.buf.borrow_mut();
while let Ok(value) = self.rx.try_recv() {
buf.push_back(value);
}
buf
}
}
}
#[cfg(target_arch = "wasm32")]
pub(crate) use web::*;
#[cfg(target_arch = "wasm32")]
mod web {
use super::*;
use crate::ecs::sys::system::SystemId;
thread_local! {
pub(crate) static WORK_ID: Cell<WorkId> = const {
Cell::new(WorkId {
wid: WorkerId::dummy(),
sid: SystemId::dummy(),
kind: TaskKind::System,
})
};
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct WorkId {
pub(crate) wid: WorkerId,
pub(crate) sid: SystemId,
pub(crate) kind: TaskKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum TaskKind {
System,
Parallel,
Async,
}
}