1use std::fs::File;
4use std::ops::Deref;
5use std::ops::DerefMut;
6use std::os::fd::{AsFd, AsRawFd, BorrowedFd};
7
8use nix::fcntl::{flock, FlockArg};
9use nix::sys::wait::{waitpid, WaitStatus};
10use nix::{
11 errno::Errno,
12 sys::signal::Signal,
13 unistd::{fork, ForkResult, Pid},
14};
15use num_threads::is_single_threaded;
16
17pub fn easy_fork() -> Result<Option<Pid>, Errno> {
23 if let Some(single) = is_single_threaded() {
24 if !single {
25 panic!("easy_fork: other threads are running, refusing to fork")
26 }
27 } else {
28 panic!("easy_fork: can't determine if other threads are running")
29 }
30 match unsafe {
31 fork()
33 }? {
34 ForkResult::Parent { child, .. } => Ok(Some(child)),
35 ForkResult::Child => Ok(None),
36 }
37}
38
39pub enum Status {
40 Normalexit(i32),
41 Signalexit(Signal),
42}
43
44pub fn waitpid_until_gone(pid: Pid) -> Result<Status, Errno> {
47 loop {
48 let st = waitpid(pid, None)?;
49 match st {
50 WaitStatus::Exited(_pid, exitcode) => return Ok(Status::Normalexit(exitcode)),
51 WaitStatus::Signaled(_pid, signal, _bool) => return Ok(Status::Signalexit(signal)),
52 _ => {} }
54 }
55}
56
57pub struct FlockGuard<'t> {
60 file: Option<&'t mut File>,
61}
62
63impl<'t> FlockGuard<'t> {
64 pub fn leak(&mut self) -> Option<&'t mut File> {
69 self.file.take()
70 }
71}
72
73impl<'t> Deref for FlockGuard<'t> {
74 type Target = File;
75
76 fn deref(&self) -> &Self::Target {
77 self.file
78 .as_ref()
79 .expect("do not dereference the FlockGuard after calling leak() on it")
80 }
81}
82
83impl<'t> DerefMut for FlockGuard<'t> {
84 fn deref_mut(&mut self) -> &mut Self::Target {
85 self.file
86 .as_mut()
87 .expect("do not dereference the FlockGuard after calling leak() on it")
88 }
89}
90
91impl<'t> Drop for FlockGuard<'t> {
92 fn drop(&mut self) {
93 if let Some(file) = &self.file {
94 let bfd: BorrowedFd = file.as_fd();
95 let fd: i32 = bfd.as_raw_fd();
96 match flock(fd, FlockArg::Unlock) {
97 Ok(()) => (),
98 Err(_e) => {
99 }
104 }
105 }
106 }
107}
108
109pub fn easy_flock(
110 file: &mut File,
111 exclusive: bool,
112 nonblock: bool,
113) -> Result<Option<FlockGuard<'_>>, Errno> {
114 let bfd: BorrowedFd = file.as_fd();
115 let fd: i32 = bfd.as_raw_fd();
116 let mode = if exclusive {
117 if nonblock {
118 FlockArg::LockExclusiveNonblock
119 } else {
120 FlockArg::LockExclusive
121 }
122 } else {
123 if nonblock {
124 FlockArg::LockSharedNonblock
125 } else {
126 FlockArg::LockShared
127 }
128 };
129 match flock(fd, mode) {
130 Ok(()) => Ok(Some(FlockGuard { file: Some(file) })),
131 Err(e) => match e {
132 Errno::EWOULDBLOCK => Ok(None),
134 _ => Err(e),
135 },
136 }
137}
138
139pub fn easy_flock_nonblocking(
140 file: &mut File,
141 exclusive: bool,
142) -> Result<Option<FlockGuard<'_>>, Errno> {
143 easy_flock(file, exclusive, true)
144}
145
146pub fn easy_flock_blocking(file: &mut File, exclusive: bool) -> Result<FlockGuard<'_>, Errno> {
147 easy_flock(file, exclusive, false)
148 .map(|v| v.expect("said blocking, thus always getting the lock"))
149}