os_sync/sem/
posix.rs

1use core::cell::UnsafeCell;
2use core::mem;
3use core::convert::TryFrom;
4
5use error_code::PosixError;
6
7///POSIX implementation of Semaphore
8///
9///Note: `wait_timeout` returns false on interrupt by signal
10pub struct Sem {
11    handle: UnsafeCell<libc::sem_t>,
12}
13
14impl super::Semaphore for Sem {
15    fn new(init: u32) -> Option<Self> {
16        let mut handle = mem::MaybeUninit::uninit();
17
18        let res = unsafe {
19            libc::sem_init(handle.as_mut_ptr(), 0, init as libc::c_uint)
20        };
21
22        match res {
23            0 => Some(Self {
24                handle: UnsafeCell::new(unsafe {
25                    handle.assume_init()
26                })
27            }),
28            _ => None,
29        }
30    }
31
32    fn wait(&self) {
33        loop {
34            let res = unsafe {
35                libc::sem_wait(self.handle.get())
36            };
37
38            if res == -1 {
39                let errno = PosixError::last();
40                debug_assert_eq!(errno.raw_code(), libc::EINTR, "Unexpected error");
41                continue;
42            }
43
44            break
45        }
46    }
47
48    fn try_wait(&self) -> bool {
49        loop {
50            let res = unsafe {
51                libc::sem_trywait(self.handle.get())
52            };
53
54            if res == -1 {
55                let errno = PosixError::last();
56                if errno.is_would_block() {
57                    break false;
58                }
59
60                debug_assert_eq!(errno.raw_code(), libc::EINTR, "Unexpected error");
61                continue;
62            }
63
64            break true
65        }
66    }
67
68    fn wait_timeout(&self, timeout: core::time::Duration) -> bool {
69        let timeout = libc::timespec {
70            tv_sec: timeout.as_secs() as libc::time_t,
71            #[cfg(target_pointer_width = "64")]
72            tv_nsec: libc::suseconds_t::from(timeout.subsec_nanos()),
73            #[cfg(not(target_pointer_width = "64"))]
74            tv_nsec: libc::suseconds_t::try_from(timeout.subsec_nanos()).unwrap_or(libc::suseconds_t::max_value()),
75        };
76
77        loop {
78            let res = unsafe {
79                libc::sem_timedwait(self.handle.get(), &timeout)
80            };
81
82            if res == -1 {
83                let errno = PosixError::last();
84                if errno.is_would_block() || errno.raw_code() == libc::ETIMEDOUT {
85                    break false;
86                }
87
88                debug_assert_eq!(errno.raw_code(), libc::EINTR, "Unexpected error");
89                continue;
90            }
91
92            break true
93        }
94    }
95
96    fn signal(&self) {
97        let res = unsafe {
98            libc::sem_post(self.handle.get())
99        };
100        debug_assert_eq!(res, 0);
101    }
102
103    fn post(&self) -> bool {
104        let mut val = 0;
105        unsafe {
106            libc::sem_getvalue(self.handle.get(), &mut val);
107        }
108
109        self.signal();
110
111        val == 0
112    }
113}
114
115impl Drop for Sem {
116    fn drop(&mut self) {
117        unsafe {
118            libc::sem_destroy(self.handle.get());
119        }
120    }
121}
122
123unsafe impl Send for Sem {}
124unsafe impl Sync for Sem {}