rustc_data_structures/flock/
unix.rs1use std::collections::hash_map::Entry;
2use std::fs::File;
3use std::os::unix::prelude::*;
4use std::path::{Path, PathBuf};
5use std::sync::{Arc, LazyLock, Mutex};
6use std::{io, mem};
7
8use rustc_hash::FxHashMap;
9
10static LOCK_REGISTRY: LazyLock<Mutex<FxHashMap<PathBuf, LockState>>> =
11 LazyLock::new(|| Mutex::new(FxHashMap::default()));
12
13enum LockState {
14 Exclusive { extra_files: Vec<File> },
19 Shared(Arc<UnlockGuard>),
23}
24
25#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Lock {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "Lock", "path",
&self.path, "file", &&self.file)
}
}Debug)]
26pub struct Lock {
27 path: PathBuf,
28 file: Option<Arc<UnlockGuard>>,
29}
30
31impl Lock {
32 pub fn try_lock(p: &Path, file: File, exclusive: bool) -> io::Result<Lock> {
33 let mut locks = LOCK_REGISTRY.lock().unwrap();
34
35 let file = match locks.entry(p.to_owned()) {
36 Entry::Occupied(mut state) => {
37 match state.get_mut() {
41 LockState::Exclusive { extra_files } => {
42 extra_files.push(file);
44
45 return Err(io::ErrorKind::WouldBlock.into());
46 }
47 LockState::Shared(file) => {
48 if exclusive {
49 return Err(io::ErrorKind::WouldBlock.into());
50 } else {
51 Arc::clone(file)
52 }
53 }
54 }
55 }
56 Entry::Vacant(vacant) => {
57 let file = Arc::new(UnlockGuard::try_lock(file, exclusive)?);
58
59 if exclusive {
60 vacant.insert(LockState::Exclusive { extra_files: ::alloc::vec::Vec::new()vec![] });
61 } else {
62 vacant.insert(LockState::Shared(Arc::clone(&file)));
63 }
64
65 file
66 }
67 };
68
69 Ok(Lock { path: p.to_owned(), file: Some(file) })
70 }
71}
72
73impl Drop for Lock {
74 fn drop(&mut self) {
75 let mut locks = LOCK_REGISTRY.lock().unwrap();
76 self.file.take().unwrap();
77 match locks.get_mut(&self.path).unwrap() {
78 LockState::Exclusive { extra_files: _ } => {
79 locks.remove(&self.path);
80 }
81 LockState::Shared(file) => {
82 if Arc::strong_count(file) == 1 {
83 locks.remove(&self.path);
84 }
85 }
86 }
87 }
88}
89
90#[derive(#[automatically_derived]
impl ::core::fmt::Debug for UnlockGuard {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f, "UnlockGuard",
"file", &&self.file)
}
}Debug)]
92struct UnlockGuard {
93 file: File,
94}
95
96impl UnlockGuard {
97 fn try_lock(file: File, exclusive: bool) -> io::Result<Self> {
98 let lock_type = if exclusive { libc::F_WRLCK } else { libc::F_RDLCK };
99
100 let mut flock: libc::flock = unsafe { mem::zeroed() };
101 #[cfg(not(all(target_os = "hurd", target_arch = "x86")))]
102 {
103 flock.l_type = lock_type as libc::c_short;
104 flock.l_whence = libc::SEEK_SET as libc::c_short;
105 }
106 #[cfg(all(target_os = "hurd", target_arch = "x86"))]
107 {
108 flock.l_type = lock_type as libc::c_int;
109 flock.l_whence = libc::SEEK_SET as libc::c_int;
110 }
111 flock.l_start = 0;
112 flock.l_len = 0;
113
114 let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLK, &flock) };
115 if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(Self { file }) }
116 }
117}
118
119impl Drop for UnlockGuard {
120 fn drop(&mut self) {
121 let mut flock: libc::flock = unsafe { mem::zeroed() };
122 #[cfg(not(all(target_os = "hurd", target_arch = "x86")))]
123 {
124 flock.l_type = libc::F_UNLCK as libc::c_short;
125 flock.l_whence = libc::SEEK_SET as libc::c_short;
126 }
127 #[cfg(all(target_os = "hurd", target_arch = "x86"))]
128 {
129 flock.l_type = libc::F_UNLCK as libc::c_int;
130 flock.l_whence = libc::SEEK_SET as libc::c_int;
131 }
132 flock.l_start = 0;
133 flock.l_len = 0;
134
135 unsafe {
136 libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock);
137 }
138 }
139}