dope_core/platform/thread/
mod.rs1use std::{cell, marker, mem, num::NonZero, ptr, vec};
2
3use dope_sys::thread;
4use o3::alloc::{self, VecExt};
5
6use crate::Errno;
7
8mod raw;
9
10union Value<T, O> {
11 task: mem::ManuallyDrop<T>,
12 output: mem::ManuallyDrop<O>,
13}
14
15pub trait Task: Send {
17 type Output: Send;
18 fn run(self) -> Self::Output;
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum Error {
23 Attributes(Errno),
24 Create(Errno),
25}
26
27pub struct Builder<T, O, P> {
29 group: raw::Group<T, O, P>,
30}
31
32impl<T, O, P> Builder<T, O, P> {
33 pub fn try_new(capacity: usize) -> Result<Self, alloc::Error> {
34 Ok(Self {
35 group: raw::Group {
36 slots: Vec::try_new(capacity)?,
37 started: 0,
38 },
39 })
40 }
41 pub fn try_push(&mut self, task: T, parent: P) -> Result<(), alloc::Error> {
42 alloc::Vacant::try_new(&mut self.group.slots)?.insert(Slot {
43 value: cell::UnsafeCell::new(Value {
44 task: mem::ManuallyDrop::new(task),
45 }),
46 thread: cell::Cell::new(mem::MaybeUninit::uninit()),
47 parent,
48 });
49 Ok(())
50 }
51 pub fn scope<R>(self, run: impl for<'scope> FnOnce(Workers<'scope, T, O, P>) -> R) -> R {
54 let mut group = self.group;
55 run(Workers {
56 group: &mut group,
57 owner: marker::PhantomData,
58 })
59 }
60}
61
62pub struct Slot<T, O, P> {
64 value: cell::UnsafeCell<Value<T, O>>,
65 thread: cell::Cell<mem::MaybeUninit<thread::Handle>>,
66 parent: P,
67}
68
69impl<T, O, P> Slot<T, O, P> {
70 pub fn parent(&self) -> &P {
71 &self.parent
72 }
73}
74
75pub struct Workers<'scope, T, O, P> {
77 group: &'scope mut raw::Group<T, O, P>,
78 owner: marker::PhantomData<fn(&'scope ()) -> &'scope ()>,
79}
80
81impl<T, O, P> Workers<'_, T, O, P> {
82 pub fn entries(&self) -> &[Slot<T, O, P>] {
83 &self.group.slots
84 }
85 pub fn parents_mut(&mut self) -> impl ExactSizeIterator<Item = &mut P> {
88 self.group.parents_mut()
89 }
90 pub fn finish(self) -> Finished<T, O, P> {
94 self.group.take()
95 }
96}
97
98pub struct Finished<T, O, P> {
100 slots: vec::IntoIter<Slot<T, O, P>>,
101}
102
103impl<T, O, P> Workers<'_, T, O, P> {
104 pub fn start(&mut self, stack: NonZero<usize>) -> Result<(), (&P, Error)>
108 where
109 T: Task<Output = O>,
110 {
111 let pending = unsafe { self.group.slots.get_unchecked(self.group.started..) };
114 let Some(first) = pending.first() else {
115 return Ok(());
116 };
117 let attr = thread::Attrs::with_stack(stack)
118 .map_err(|error| (&first.parent, Error::Attributes(error)))?;
119 for slot in pending {
120 let entry = unsafe { thread::raw::Entry::new(ptr::NonNull::from(slot)) };
126 let handle = entry
127 .spawn(&attr)
128 .map_err(|error| (&slot.parent, Error::Create(error)))?;
129 slot.thread.set(mem::MaybeUninit::new(handle));
130 self.group.started += 1;
131 }
132 Ok(())
133 }
134}
135
136impl<T, O, P> Iterator for Finished<T, O, P> {
137 type Item = (P, O);
138
139 fn next(&mut self) -> Option<Self::Item> {
142 let slot = self.slots.next()?;
143 let Slot { value, parent, .. } = slot;
144 Some((
147 parent,
148 mem::ManuallyDrop::into_inner(unsafe { value.into_inner().output }),
149 ))
150 }
151 fn size_hint(&self) -> (usize, Option<usize>) {
152 self.slots.size_hint()
153 }
154}
155impl<T, O, P> ExactSizeIterator for Finished<T, O, P> {}
156
157impl<T, O, P> Drop for Finished<T, O, P> {
158 fn drop(&mut self) {
159 for slot in self.slots.as_mut_slice() {
160 unsafe { mem::ManuallyDrop::drop(&mut slot.value.get_mut().output) };
163 }
164 }
165}