Skip to main content

relibc/sync/
semaphore.rs

1// From https://www.remlab.net/op/futex-misc.shtml
2//TODO: improve implementation
3
4use crate::{
5    error::{Errno, Result},
6    header::{
7        errno,
8        time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec, timespec_realtime_to_monotonic},
9    },
10    platform::{
11        Pal, Sys,
12        types::{c_uint, clockid_t},
13    },
14    sync::FutexAtomicTy,
15};
16
17use core::sync::atomic::{AtomicU32, Ordering};
18
19pub struct Semaphore {
20    count: AtomicU32,
21}
22
23impl Semaphore {
24    pub const fn new(value: c_uint) -> Self {
25        Self {
26            count: AtomicU32::new(value),
27        }
28    }
29
30    // TODO: Acquire-Release ordering?
31
32    pub fn post(&self, count: c_uint) {
33        self.count.fetch_add(count, Ordering::SeqCst);
34        // TODO: notify one?
35        crate::sync::futex_wake(&self.count, i32::MAX);
36    }
37
38    pub fn try_wait(&self) -> bool {
39        loop {
40            let value = self.count.load(Ordering::SeqCst);
41
42            if value == 0 {
43                return false;
44            }
45
46            if self
47                .count
48                .compare_exchange_weak(value, value - 1, Ordering::SeqCst, Ordering::SeqCst)
49                .is_ok()
50            {
51                // Acquired
52                return true;
53            }
54            // Try again (as long as value > 0)
55        }
56    }
57
58    pub fn wait(&self, timeout_opt: Option<&timespec>, clock_id: clockid_t) -> Result<()> {
59        loop {
60            if self.try_wait() {
61                return Ok(());
62            }
63            // value must be zero
64            if let Some(timeout) = timeout_opt {
65                let relative = match clock_id {
66                    // FUTEX expect monotonic clock
67                    CLOCK_MONOTONIC => timeout.clone(),
68                    CLOCK_REALTIME => timespec_realtime_to_monotonic(timeout)?,
69                    _ => return Err(Errno(errno::EINVAL)),
70                };
71                unsafe { Sys::futex_wait(self.count.ptr(), 0, Some(&relative))? };
72            } else {
73                // Use futex to wait for the next change, without a timeout
74                unsafe { Sys::futex_wait(self.count.ptr(), 0, None)? };
75            }
76        }
77    }
78    pub fn value(&self) -> c_uint {
79        self.count.load(Ordering::SeqCst)
80    }
81}