scallop/
pool.rs

1use std::ffi::CString;
2use std::fs::File;
3use std::os::fd::AsFd;
4
5use nix::errno::Errno;
6use nix::unistd::{dup2_stderr, dup2_stdout};
7
8use crate::Error;
9
10/// Redirect stdout and stderr to a given raw file descriptor.
11pub fn redirect_output<T: AsFd>(fd: T) -> crate::Result<()> {
12    dup2_stdout(&fd).map_err(|e| Error::IO(e.to_string()))?;
13    dup2_stderr(&fd).map_err(|e| Error::IO(e.to_string()))?;
14    Ok(())
15}
16
17/// Suppress stdout and stderr.
18pub fn suppress_output() -> crate::Result<()> {
19    let f = File::options().write(true).open("/dev/null")?;
20    redirect_output(&f)?;
21    Ok(())
22}
23
24/// Semaphore wrapping libc named semaphore calls.
25pub struct NamedSemaphore {
26    sem: *mut libc::sem_t,
27    size: u32,
28}
29
30impl NamedSemaphore {
31    pub fn new<S: AsRef<str>>(name: S, size: usize) -> crate::Result<Self> {
32        let name = CString::new(name.as_ref()).unwrap();
33        let size: u32 = size
34            .try_into()
35            .map_err(|_| Error::Base(format!("pool too large: {size}")))?;
36
37        let sem = unsafe { libc::sem_open(name.as_ptr(), libc::O_CREAT, 0o600, size) };
38        if !sem.is_null() {
39            unsafe { libc::sem_unlink(name.as_ptr()) };
40            Ok(Self { sem, size })
41        } else {
42            let err = Errno::last_raw();
43            Err(Error::Base(format!("sem_open() failed: {err}")))
44        }
45    }
46
47    pub fn acquire(&mut self) -> crate::Result<()> {
48        if unsafe { libc::sem_wait(self.sem) } == 0 {
49            Ok(())
50        } else {
51            // grcov-excl-start: only errors on signal handler interrupt
52            let err = Errno::last_raw();
53            Err(Error::Base(format!("sem_wait() failed: {err}")))
54        } // grcov-excl-stop
55    }
56
57    pub fn release(&mut self) -> crate::Result<()> {
58        if unsafe { libc::sem_post(self.sem) } == 0 {
59            Ok(())
60        } else {
61            let err = Errno::last_raw();
62            Err(Error::Base(format!("sem_post() failed: {err}")))
63        }
64    }
65
66    pub fn wait(&mut self) -> crate::Result<()> {
67        for _ in 0..self.size {
68            self.acquire()?;
69        }
70        Ok(())
71    }
72}
73
74impl Drop for NamedSemaphore {
75    fn drop(&mut self) {
76        unsafe {
77            libc::sem_close(self.sem);
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn semaphore() {
88        // exceed max semaphore value
89        let size = u32::MAX.try_into().unwrap();
90        assert!(NamedSemaphore::new("test", size).is_err());
91
92        // max value is i32::MAX
93        let size = i32::MAX.try_into().unwrap();
94        let mut sem = NamedSemaphore::new("test", size).unwrap();
95        // overflow semaphore value
96        assert!(sem.release().is_err());
97
98        // acquire then release
99        sem.acquire().unwrap();
100        assert!(sem.release().is_ok());
101
102        // acquire all
103        let mut sem = NamedSemaphore::new("test", 10).unwrap();
104        sem.wait().unwrap();
105    }
106}