simplelock/impls/
semaphore.rs1use crate::*;
10
11use nix::{fcntl::OFlag, sys::stat::Mode};
15
16pub struct SemaphoreLock {
19 sem: safe_wrapper::NamedSem,
21
22 have_lock: bool,
26}
27
28const INITIAL_VALUE: i32 = 1;
29const FLAGS: OFlag = OFlag::O_CREAT;
30
31fn verify_name(name: impl Into<String>) -> SimpleLockResult<String> {
40 let mut test = name.into();
41 if !test.starts_with("/") {
42 test.insert_str(0, "/");
43 }
44
45 if test.len() < 2 || test.len() > 250 || test.split('/').count() > 2 {
46 Err(SimpleLockError::InvalidName)
47 } else {
48 Ok(test)
49 }
50}
51
52impl SemaphoreLock {
53 pub fn new(name: impl Into<String>) -> SimpleLockResult<SemaphoreLock> {
55 let name = verify_name(name)?;
56 let mode = Mode::S_IRWXU | Mode::S_IRWXO;
57 let sem = safe_wrapper::sem_open(&name, &FLAGS, mode, INITIAL_VALUE)?;
58 Ok(SemaphoreLock {
59 sem,
60 have_lock: false,
61 })
62 }
63
64 pub fn name(&self) -> String {
66 self.sem.name()
67 }
68}
69
70impl ConcreteLock for SemaphoreLock {
71 fn status(&self) -> SimpleLockResult<LockStatus> {
72 if self.have_lock {
73 Ok(LockStatus::Mine)
74 } else if safe_wrapper::sem_getvalue(&self.sem)? != INITIAL_VALUE {
75 Ok(LockStatus::Taken)
76 } else {
77 Ok(LockStatus::Open)
78 }
79 }
80
81 fn try_lock(&mut self) -> SimpleLockResult<()> {
82 if self.have_lock {
83 return Ok(());
84 }
85 safe_wrapper::sem_trywait(&self.sem)
86 .map_err(|e| e.into())
87 .and_then(|_| {
88 self.have_lock = true;
89 Ok(())
90 })
91 }
92
93 fn hang_lock(&mut self) -> SimpleLockResult<()> {
94 if self.have_lock {
95 return Ok(());
96 }
97 safe_wrapper::sem_wait(&self.sem)
98 .map_err(|e| e.into())
99 .and_then(|_| {
100 self.have_lock = true;
101 Ok(())
102 })
103 }
104
105 fn try_unlock(&mut self) -> SimpleLockResult<()> {
106 if !self.have_lock {
107 return Ok(());
108 }
109 safe_wrapper::sem_post(&self.sem)
110 .map_err(|e| e.into())
111 .and_then(|_| {
112 self.have_lock = false;
113 Ok(())
114 })
115 }
116}
117
118mod safe_wrapper {
120 use crate::SimpleLockError;
121 use nix::{errno::Errno, fcntl::OFlag, sys::stat::Mode, Error, NixPath};
122
123 impl From<Error> for SimpleLockError {
124 fn from(e: Error) -> Self {
125 match e {
126 Error::InvalidPath => SimpleLockError::InvalidName,
129 Error::InvalidUtf8 => SimpleLockError::InvalidName,
130
131 Error::Sys(errno) => errno.into(),
133
134 Error::UnsupportedOperation => {
137 SimpleLockError::UnknownError("nix::UnsupportedOperation".into())
138 }
139 }
140 }
141 }
142
143 impl From<Errno> for SimpleLockError {
144 fn from(e: Errno) -> Self {
145 match e {
146 Errno::EACCES => SimpleLockError::PermissionDenied,
148
149 Errno::EMFILE => SimpleLockError::PermissionDenied,
151 Errno::ENFILE => SimpleLockError::PermissionDenied,
153 Errno::ENOMEM => SimpleLockError::PermissionDenied,
155
156 Errno::EINVAL => SimpleLockError::InvalidName,
160
161 Errno::ENAMETOOLONG => SimpleLockError::InvalidName,
163
164 Errno::EINTR => SimpleLockError::UnknownError("Signal interrupt.".into()),
166
167 _ => SimpleLockError::UnknownError(format!("{:?}: {}", e, e.desc())),
169 }
170 }
171 }
172
173 pub struct NamedSem {
175 name: String,
176 sem: *mut libc::sem_t,
177 }
178 impl Drop for NamedSem {
179 fn drop(&mut self) {
180 let _ = sem_post(self);
182 let _ = sem_close(self);
183 }
184 }
185 impl NamedSem {
186 pub fn name(&self) -> String {
187 self.name.clone()
188 }
189 }
190
191 pub fn sem_getvalue(sem: &NamedSem) -> nix::Result<i32> {
192 let mut res: i32 = 0;
193 if unsafe { libc::sem_getvalue(sem.sem, &mut res) == libc::EXIT_SUCCESS } {
194 Ok(res)
195 } else {
196 Err(nix::Error::Sys(Errno::last()).into())
197 }
198 }
199
200 pub fn sem_open(name: &String, oflag: &OFlag, mode: Mode, value: i32) -> nix::Result<NamedSem> {
201 name.as_bytes().with_nix_path(|cstr| unsafe {
202 let sem: *mut libc::sem_t = libc::sem_open(
203 cstr.as_ptr(),
204 oflag.bits(),
205 mode.bits() as libc::mode_t,
206 value,
207 );
208 if sem == libc::SEM_FAILED {
209 Err(nix::Error::Sys(Errno::last()))
210 } else {
211 Ok(NamedSem {
212 name: name.clone(),
213 sem,
214 })
215 }
216 })?
217 }
218
219 pub fn sem_wait(sem: &NamedSem) -> nix::Result<()> {
220 if unsafe { libc::sem_wait(sem.sem) == libc::EXIT_SUCCESS } {
221 Ok(())
222 } else {
223 Err(nix::Error::Sys(Errno::last()).into())
224 }
225 }
226
227 pub fn sem_trywait(sem: &NamedSem) -> nix::Result<()> {
228 if unsafe { libc::sem_trywait(sem.sem) == libc::EXIT_SUCCESS } {
229 Ok(())
230 } else {
231 Err(nix::Error::Sys(Errno::last()).into())
232 }
233 }
234
235 pub fn sem_post(sem: &NamedSem) -> nix::Result<()> {
236 if unsafe { libc::sem_post(sem.sem) == libc::EXIT_SUCCESS } {
237 Ok(())
238 } else {
239 Err(nix::Error::Sys(Errno::last()).into())
240 }
241 }
242
243 pub fn sem_close(sem: &mut NamedSem) -> nix::Result<()> {
244 if unsafe { libc::sem_close(sem.sem) == libc::EXIT_SUCCESS } {
245 Ok(())
246 } else {
247 Err(nix::Error::Sys(Errno::last()).into())
248 }
249 }
250}
251
252#[cfg(test)]
253mod _test {
254 use super::*;
255
256 #[test]
257 fn good_names() {
258 assert_eq!(true, verify_name("abc").is_ok(), "lowercase");
260 assert_eq!(true, verify_name("123").is_ok(), "numbers");
261 assert_eq!(true, verify_name("ABC").is_ok(), "capitals");
262 assert_eq!(true, verify_name("a-c").is_ok(), "dash");
263 assert_eq!(true, verify_name("a_c").is_ok(), "underscore");
264
265 assert_eq!(true, verify_name("/ab").is_ok(), "prefix-slash");
267 }
268
269 #[test]
270 fn bad_names() {
271 assert_eq!(false, verify_name("").is_ok(), "too short");
272 assert_eq!(false, verify_name("a".repeat(251)).is_ok(), "too long");
273 assert_eq!(false, verify_name("a/").is_ok(), "slash within name");
274 }
275}