1use crate::util::errors::CodeError;
7use std::{fs::File, io};
8
9pub struct FileLock {
10 file: File,
11 #[cfg(windows)]
12 overlapped: winapi::um::minwinbase::OVERLAPPED,
13}
14
15#[cfg(windows)] unsafe impl Send for FileLock {}
17
18pub enum Lock {
19 Acquired(FileLock),
20 AlreadyLocked(File),
21}
22
23#[cfg(windows)]
28pub const PREFIX_LOCKED_BYTES: usize = 1;
29
30#[cfg(unix)]
31pub const PREFIX_LOCKED_BYTES: usize = 0;
32
33impl FileLock {
34 #[cfg(windows)]
35 pub fn acquire(file: File) -> Result<Lock, CodeError> {
36 use std::os::windows::prelude::AsRawHandle;
37 use winapi::{
38 shared::winerror::{ERROR_IO_PENDING, ERROR_LOCK_VIOLATION},
39 um::{
40 fileapi::LockFileEx,
41 minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY},
42 },
43 };
44
45 let handle = file.as_raw_handle();
46 let (overlapped, ok) = unsafe {
47 let mut overlapped = std::mem::zeroed();
48 let ok = LockFileEx(
49 handle,
50 LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
51 0,
52 PREFIX_LOCKED_BYTES as u32,
53 0,
54 &mut overlapped,
55 );
56
57 (overlapped, ok)
58 };
59
60 if ok != 0 {
61 return Ok(Lock::Acquired(Self { file, overlapped }));
62 }
63
64 let err = io::Error::last_os_error();
65 let raw = err.raw_os_error();
66 if raw == Some(ERROR_IO_PENDING as i32) || raw == Some(ERROR_LOCK_VIOLATION as i32) {
69 return Ok(Lock::AlreadyLocked(file));
70 }
71
72 Err(CodeError::SingletonLockfileOpenFailed(err))
73 }
74
75 #[cfg(unix)]
76 pub fn acquire(file: File) -> Result<Lock, CodeError> {
77 use std::os::unix::io::AsRawFd;
78
79 let fd = file.as_raw_fd();
80 let res = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
81 if res == 0 {
82 return Ok(Lock::Acquired(Self { file }));
83 }
84
85 let err = io::Error::last_os_error();
86 if err.kind() == io::ErrorKind::WouldBlock {
87 return Ok(Lock::AlreadyLocked(file));
88 }
89
90 Err(CodeError::SingletonLockfileOpenFailed(err))
91 }
92
93 pub fn file(&self) -> &File {
94 &self.file
95 }
96
97 pub fn file_mut(&mut self) -> &mut File {
98 &mut self.file
99 }
100}
101
102impl Drop for FileLock {
103 #[cfg(windows)]
104 fn drop(&mut self) {
105 use std::os::windows::prelude::AsRawHandle;
106 use winapi::um::fileapi::UnlockFileEx;
107
108 unsafe {
109 UnlockFileEx(
110 self.file.as_raw_handle(),
111 0,
112 u32::MAX,
113 u32::MAX,
114 &mut self.overlapped,
115 )
116 };
117 }
118
119 #[cfg(unix)]
120 fn drop(&mut self) {
121 use std::os::unix::io::AsRawFd;
122
123 unsafe { libc::flock(self.file.as_raw_fd(), libc::LOCK_UN) };
124 }
125}