use alloc::{boxed::Box, sync::Arc, vec::Vec};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use rt::sync::SpinLock;
pub const THR_MAX: usize = 64;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cancelled;
pub struct JobCtl {
count: usize,
done: AtomicUsize,
cancel: AtomicBool,
running: AtomicBool,
}
impl JobCtl {
fn new(count: usize) -> Self {
Self {
count,
done: AtomicUsize::new(0),
cancel: AtomicBool::new(false),
running: AtomicBool::new(false),
}
}
pub fn cancel(&self) {
self.cancel.store(true, Ordering::Relaxed);
}
pub fn cancelled(&self) -> bool {
self.cancel.load(Ordering::Relaxed)
}
pub fn count(&self) -> u64 {
self.count as u64
}
pub fn items_done(&self) -> u64 {
self.done.load(Ordering::Relaxed) as u64
}
}
struct Record {
ctl: JobCtl,
function: Option<Box<dyn Fn(usize, &JobCtl) + Send + Sync>>,
}
static QUEUE: SpinLock<Vec<Arc<Record>>> = SpinLock::new(Vec::new());
fn step(record: &Record) -> bool {
let ctl = &record.ctl;
if !ctl.running.load(Ordering::Relaxed) {
return false;
}
let i = ctl.done.load(Ordering::Relaxed);
if i >= ctl.count || ctl.cancelled() || record.function.is_none() {
ctl.running.store(false, Ordering::Relaxed);
crate::wake();
return false;
}
if let Some(function) = &record.function {
function(i, ctl);
}
ctl.done.store(i + 1, Ordering::Relaxed);
if i + 1 >= ctl.count || ctl.cancelled() {
ctl.running.store(false, Ordering::Relaxed);
crate::wake();
}
true
}
pub fn pump_jobs(items: usize) {
for _ in 0..items {
let record = {
let mut q = QUEUE.lock();
if q.is_empty() {
None
} else {
Some(q.remove(0))
}
};
let Some(record) = record else {
break;
};
let _ = step(&record);
if record.ctl.running.load(Ordering::Relaxed) {
QUEUE.lock().push(record);
}
}
}
pub fn thr_init(_count: u32) -> u32 {
0
}
pub fn thr_count() -> u32 {
0
}
pub fn thr_shutdown() {
let queue = core::mem::take(&mut *QUEUE.lock());
for record in queue {
record.ctl.cancel();
let _ = step(&record);
}
}
pub fn thr_self() -> usize {
0
}
pub fn parallel_for(count: usize, f: &(dyn Fn(usize, &JobCtl) + Sync)) -> Result<(), Cancelled> {
let ctl = JobCtl::new(count);
for i in 0..count {
if ctl.cancelled() {
break;
}
f(i, &ctl);
ctl.done.store(i + 1, Ordering::Relaxed);
}
if ctl.cancelled() {
Err(Cancelled)
} else {
Ok(())
}
}
pub struct Job {
record: Arc<Record>,
}
impl Default for Job {
fn default() -> Self {
Self::new()
}
}
impl Job {
pub fn new() -> Self {
Self {
record: Arc::new(Record {
ctl: JobCtl::new(0),
function: None,
}),
}
}
pub fn set(&mut self, count: u64, f: impl Fn(usize, &JobCtl) + Send + Sync + 'static) -> bool {
if !self.done() || count > usize::MAX as u64 {
return false;
}
self.record = Arc::new(Record {
ctl: JobCtl::new(count as usize),
function: Some(Box::new(f)),
});
true
}
pub fn set_count(&mut self, count: u64) -> bool {
if !self.done() || count > usize::MAX as u64 {
return false;
}
QUEUE.lock().retain(|r| !Arc::ptr_eq(r, &self.record));
let Some(record) = Arc::get_mut(&mut self.record) else {
return false;
};
record.ctl.count = count as usize;
true
}
pub fn start(&self) -> bool {
if !self.done() {
return false;
}
let ctl = &self.record.ctl;
ctl.done.store(0, Ordering::Relaxed);
ctl.cancel.store(false, Ordering::Relaxed);
QUEUE.lock().retain(|r| !Arc::ptr_eq(r, &self.record));
if ctl.count == 0 || self.record.function.is_none() {
crate::wake();
return true;
}
ctl.running.store(true, Ordering::Relaxed);
QUEUE.lock().push(self.record.clone());
crate::wake();
true
}
pub fn wait(&self) -> Result<(), Cancelled> {
while step(&self.record) {}
if self.record.ctl.cancelled() {
Err(Cancelled)
} else {
Ok(())
}
}
pub fn cancel(&self) {
self.record.ctl.cancel();
}
pub fn done(&self) -> bool {
!self.record.ctl.running.load(Ordering::Relaxed)
}
pub fn items_done(&self) -> u64 {
self.record.ctl.items_done()
}
pub fn count(&self) -> u64 {
self.record.ctl.count()
}
}
impl Drop for Job {
fn drop(&mut self) {
self.cancel();
let _ = self.wait();
}
}