unix.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::errors::{ Error, Result }; use crate::file_ext::{ FileExt }; use errno::{ Errno, errno }; use std::fs::{ File }; use std::os::raw::{ c_int }; use std::os::fd::{ AsRawFd }; impl FileExt for File { type LockGuard = LockGuard; fn lock_shared(&self) -> Result<Self::LockGuard> { lock(self.as_raw_fd(), LOCK_SH) } fn lock_exclusive(&self) -> Result<Self::LockGuard> { lock(self.as_raw_fd(), LOCK_EX) } fn try_lock_shared(&self) -> Result<Self::LockGuard> { try_lock(self.as_raw_fd(), LOCK_SH) } fn try_lock_exclusive(&self) -> Result<Self::LockGuard> { try_lock(self.as_raw_fd(), LOCK_EX) } } #[derive(Debug)] #[must_use = "Not retaining the guard object will result in the file being immediately unlocked"] pub struct LockGuard { fd: c_int, } impl Drop for LockGuard { fn drop(&mut self) { unsafe { flock(self.fd, LOCK_UN); } } } fn lock(fd: c_int, mode: c_int) -> Result<LockGuard> { let status = unsafe { flock(fd, mode) }; match status { 0 => Ok(LockGuard { fd }), _ => Err(Error::LockFailed(errno())), } } fn try_lock(fd: c_int, mode: c_int) -> Result<LockGuard> { let status = unsafe { flock(fd, mode | LOCK_NB) }; match status { 0 => Ok(LockGuard { fd }), _ => { let e = errno(); if e == Errno(EAGAIN) || e == Errno(EWOULDBLOCK) { Err(Error::AlreadyLocked) } else { Err(Error::LockFailed(e)) } }, } } #[cfg(target_os = "linux")] const EAGAIN: c_int = 11; #[cfg(target_os = "macos")] const EAGAIN: c_int = 35; // No default value because it's better to not compile than to silently use // an incorrect error code // Defined because on some platforms it might not be EAGAIN, and this way // we can just add a new `#[cfg(target_os = "...")]` #[cfg(any(target_os = "linux", target_os = "macos"))] const EWOULDBLOCK: c_int = EAGAIN; const LOCK_SH: c_int = 0x01; const LOCK_EX: c_int = 0x02; const LOCK_NB: c_int = 0x04; const LOCK_UN: c_int = 0x08; extern "C" { fn flock(fd: c_int, op: c_int) -> c_int; }