twizzler_abi/syscall/
thread_sync.rs

1use core::{
2    ptr,
3    sync::atomic::{AtomicU32, AtomicU64},
4    time::Duration,
5};
6
7use bitflags::bitflags;
8use twizzler_rt_abi::error::TwzError;
9
10use super::{convert_codes_to_result, twzerr, Syscall};
11use crate::{arch::syscall::raw_syscall, object::ObjID};
12#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
13#[repr(u32)]
14/// Possible operations the kernel can perform when looking at the supplies reference and the
15/// supplied value. If the operation `*reference OP value` evaluates to true (or false if the INVERT
16/// flag is passed), then the thread is put
17/// to sleep.
18pub enum ThreadSyncOp {
19    /// Compare for equality
20    Equal = 0,
21}
22
23impl ThreadSyncOp {
24    /// Apply the operation to two values, returning the result.
25    pub fn check<T: Eq + PartialEq + Ord + PartialOrd>(
26        &self,
27        a: T,
28        b: T,
29        flags: ThreadSyncFlags,
30    ) -> bool {
31        let res = match self {
32            Self::Equal => a == b,
33        };
34
35        if flags.contains(ThreadSyncFlags::INVERT) {
36            !res
37        } else {
38            res
39        }
40    }
41}
42
43bitflags! {
44    /// Flags to pass to sys_thread_sync.
45    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46    pub struct ThreadSyncFlags: u32 {
47        /// Invert the decision test for sleeping the thread.
48        const INVERT = 1;
49    }
50}
51
52#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
53#[repr(C)]
54/// A reference to a piece of data. May either be a non-realized persistent reference or a virtual
55/// address.
56pub enum ThreadSyncReference {
57    ObjectRef(ObjID, usize),
58    Virtual(*const AtomicU64),
59    Virtual32(*const AtomicU32),
60}
61unsafe impl Send for ThreadSyncReference {}
62
63impl ThreadSyncReference {
64    pub fn load(&self) -> u64 {
65        match self {
66            ThreadSyncReference::ObjectRef(_, _) => todo!(),
67            ThreadSyncReference::Virtual(p) => {
68                unsafe { &**p }.load(core::sync::atomic::Ordering::SeqCst)
69            }
70            ThreadSyncReference::Virtual32(p) => unsafe { &**p }
71                .load(core::sync::atomic::Ordering::SeqCst)
72                .into(),
73        }
74    }
75
76    pub fn address(&self) -> Option<*mut u8> {
77        match self {
78            ThreadSyncReference::ObjectRef(_, _) => None,
79            ThreadSyncReference::Virtual(p) => Some(*p as *mut u8),
80            ThreadSyncReference::Virtual32(p) => Some(*p as *mut u8),
81        }
82    }
83}
84
85#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
86#[repr(C)]
87/// Specification for a thread sleep request.
88pub struct ThreadSyncSleep {
89    /// Reference to an atomic u64 that we will compare to.
90    pub reference: ThreadSyncReference,
91    /// The value used for the comparison.
92    pub value: u64,
93    /// The operation to compare *reference and value to.
94    pub op: ThreadSyncOp,
95    /// Flags to apply to this sleep request.
96    pub flags: ThreadSyncFlags,
97}
98
99impl From<(&AtomicU64, u64)> for ThreadSyncSleep {
100    fn from((reference, value): (&AtomicU64, u64)) -> Self {
101        Self {
102            reference: ThreadSyncReference::Virtual(reference),
103            value,
104            op: ThreadSyncOp::Equal,
105            flags: ThreadSyncFlags::empty(),
106        }
107    }
108}
109
110impl From<(*const AtomicU64, u64)> for ThreadSyncSleep {
111    fn from((reference, value): (*const AtomicU64, u64)) -> Self {
112        Self {
113            reference: ThreadSyncReference::Virtual(reference),
114            value,
115            op: ThreadSyncOp::Equal,
116            flags: ThreadSyncFlags::empty(),
117        }
118    }
119}
120
121#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
122#[repr(C)]
123/// Specification for a thread wake request.
124pub struct ThreadSyncWake {
125    /// Reference to the word for which we will wake up threads that have gone to sleep.
126    pub reference: ThreadSyncReference,
127    /// Number of threads to wake up.
128    pub count: usize,
129}
130
131impl ThreadSyncSleep {
132    /// Construct a new thread sync sleep request. The kernel will compare the 64-bit data at
133    /// `*reference` with the passed `value` using `op` (and optionally inverting the result). If
134    /// true, the kernel will put the thread to sleep until another thread comes along and performs
135    /// a wake request on that same word of memory.
136    ///
137    /// References always refer to a particular 64-bit value inside of an object. If a virtual
138    /// address is passed as a reference, it is first converted to an object-offset pair based on
139    /// the current object mapping of the address space.
140    pub fn new(
141        reference: ThreadSyncReference,
142        value: u64,
143        op: ThreadSyncOp,
144        flags: ThreadSyncFlags,
145    ) -> Self {
146        Self {
147            reference,
148            value,
149            op,
150            flags,
151        }
152    }
153
154    pub fn ready(&self) -> bool {
155        let st = self.reference.load();
156        match self.op {
157            ThreadSyncOp::Equal => st != self.value,
158        }
159    }
160}
161
162impl ThreadSyncWake {
163    /// Construct a new thread wake request. The reference works the same was as in
164    /// [ThreadSyncSleep]. The kernel will wake up `count` threads that are sleeping on this
165    /// particular word of object memory. If you want to wake up all threads, you can supply
166    /// `usize::MAX`.
167    pub fn new(reference: ThreadSyncReference, count: usize) -> Self {
168        Self { reference, count }
169    }
170}
171
172/// Result of sync operations.
173pub type ThreadSyncResult = Result<usize, TwzError>;
174
175#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
176#[repr(C)]
177/// Either a sleep or wake request. The syscall comprises of a number of either sleep or wake
178/// requests.
179pub enum ThreadSync {
180    Sleep(ThreadSyncSleep, ThreadSyncResult),
181    Wake(ThreadSyncWake, ThreadSyncResult),
182}
183
184impl ThreadSync {
185    /// Build a sleep request.
186    pub fn new_sleep(sleep: ThreadSyncSleep) -> Self {
187        Self::Sleep(sleep, Ok(0))
188    }
189
190    /// Build a wake request.
191    pub fn new_wake(wake: ThreadSyncWake) -> Self {
192        Self::Wake(wake, Ok(0))
193    }
194
195    /// Get the result of the thread sync operation.
196    pub fn get_result(&self) -> ThreadSyncResult {
197        match self {
198            ThreadSync::Sleep(_, e) => *e,
199            ThreadSync::Wake(_, e) => *e,
200        }
201    }
202
203    pub fn ready(&self) -> bool {
204        match self {
205            ThreadSync::Sleep(o, _) => o.ready(),
206            ThreadSync::Wake(_, _) => true,
207        }
208    }
209}
210
211/// Perform a number of [ThreadSync] operations, either waking of threads waiting on words of
212/// memory, or sleeping this thread on one or more words of memory (or both). The order these
213/// requests are processed in is undefined.
214///
215/// The caller may optionally specify a timeout, causing this thread to sleep for at-most that
216/// Duration. However, the exact time is not guaranteed (it may be less if the thread is woken up,
217/// or slightly more due to scheduling uncertainty). If no operations are specified, the thread will
218/// sleep until the timeout expires.
219///
220/// Returns either Ok(ready_count), indicating how many operations were immediately ready, or
221/// Err([TwzError]), indicating failure. After return, the kernel may have modified the
222/// ThreadSync entries to indicate additional information about each request, with Err to indicate
223/// error and Ok(n) to indicate success. For sleep requests, n is 0 if the operation went to sleep
224/// or 1 otherwise. For wakeup requests, n indicates the number of threads woken up by this
225/// operation.
226///
227/// Note that spurious wakeups are possible, and that even if a timeout occurs the function may
228/// return Ok(0).
229pub fn sys_thread_sync(
230    operations: &mut [ThreadSync],
231    timeout: Option<Duration>,
232) -> Result<usize, TwzError> {
233    let ptr = operations.as_mut_ptr();
234    let count = operations.len();
235    let timeout = timeout
236        .as_ref()
237        .map_or(ptr::null(), |t| t as *const Duration);
238
239    let (code, val) = unsafe {
240        raw_syscall(
241            Syscall::ThreadSync,
242            &[ptr as u64, count as u64, timeout as u64],
243        )
244    };
245    convert_codes_to_result(code, val, |c, _| c != 0, |_, v| v as usize, twzerr)
246}