Mercurial > hg
annotate rust/hg-core/src/lock.rs @ 48614:3efc8644dd00
test-http-bad-server: refactor the reading logic to avoid early return
Our ultimate goal is to add another way to define the connection needs to be
closed. To do so, we need the "read" code to be more unified.
Differential Revision: https://phab.mercurial-scm.org/D12045
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Fri, 21 Jan 2022 11:15:56 +0100 |
parents | 5734b03ecf3e |
children | 59be65b7cdfd |
rev | line source |
---|---|
48417
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
1 //! Filesystem-based locks for local repositories |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
2 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
3 use crate::errors::HgError; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
4 use crate::errors::HgResultExt; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
5 use crate::utils::StrExt; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
6 use crate::vfs::Vfs; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
7 use std::io; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
8 use std::io::ErrorKind; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
9 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
10 #[derive(derive_more::From)] |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
11 pub enum LockError { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
12 AlreadyHeld, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
13 #[from] |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
14 Other(HgError), |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
15 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
16 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
17 /// Try to call `f` with the lock acquired, without waiting. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
18 /// |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
19 /// If the lock is aready held, `f` is not called and `LockError::AlreadyHeld` |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
20 /// is returned. `LockError::Io` is returned for any unexpected I/O error |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
21 /// accessing the lock file, including for removing it after `f` was called. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
22 /// The return value of `f` is dropped in that case. If all is successful, the |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
23 /// return value of `f` is forwarded. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
24 pub fn try_with_lock_no_wait<R>( |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
25 hg_vfs: Vfs, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
26 lock_filename: &str, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
27 f: impl FnOnce() -> R, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
28 ) -> Result<R, LockError> { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
29 let our_lock_data = &*OUR_LOCK_DATA; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
30 for _retry in 0..5 { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
31 match make_lock(hg_vfs, lock_filename, our_lock_data) { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
32 Ok(()) => { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
33 let result = f(); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
34 unlock(hg_vfs, lock_filename)?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
35 return Ok(result); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
36 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
37 Err(HgError::IoError { error, .. }) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
38 if error.kind() == ErrorKind::AlreadyExists => |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
39 { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
40 let lock_data = read_lock(hg_vfs, lock_filename)?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
41 if lock_data.is_none() { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
42 // Lock was apparently just released, retry acquiring it |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
43 continue; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
44 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
45 if !lock_should_be_broken(&lock_data) { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
46 return Err(LockError::AlreadyHeld); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
47 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
48 // The lock file is left over from a process not running |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
49 // anymore. Break it, but with another lock to |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
50 // avoid a race. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
51 break_lock(hg_vfs, lock_filename)?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
52 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
53 // Retry acquiring |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
54 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
55 Err(error) => Err(error)?, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
56 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
57 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
58 Err(LockError::AlreadyHeld) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
59 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
60 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
61 fn break_lock(hg_vfs: Vfs, lock_filename: &str) -> Result<(), LockError> { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
62 try_with_lock_no_wait(hg_vfs, &format!("{}.break", lock_filename), || { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
63 // Check again in case some other process broke and |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
64 // acquired the lock in the meantime |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
65 let lock_data = read_lock(hg_vfs, lock_filename)?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
66 if !lock_should_be_broken(&lock_data) { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
67 return Err(LockError::AlreadyHeld); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
68 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
69 Ok(hg_vfs.remove_file(lock_filename)?) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
70 })? |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
71 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
72 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
73 #[cfg(unix)] |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
74 fn make_lock( |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
75 hg_vfs: Vfs, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
76 lock_filename: &str, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
77 data: &str, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
78 ) -> Result<(), HgError> { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
79 // Use a symbolic link because creating it is atomic. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
80 // The link’s "target" contains data not representing any path. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
81 let fake_symlink_target = data; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
82 hg_vfs.create_symlink(lock_filename, fake_symlink_target) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
83 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
84 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
85 fn read_lock( |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
86 hg_vfs: Vfs, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
87 lock_filename: &str, |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
88 ) -> Result<Option<String>, HgError> { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
89 let link_target = |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
90 hg_vfs.read_link(lock_filename).io_not_found_as_none()?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
91 if let Some(target) = link_target { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
92 let data = target |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
93 .into_os_string() |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
94 .into_string() |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
95 .map_err(|_| HgError::corrupted("non-UTF-8 lock data"))?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
96 Ok(Some(data)) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
97 } else { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
98 Ok(None) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
99 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
100 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
101 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
102 fn unlock(hg_vfs: Vfs, lock_filename: &str) -> Result<(), HgError> { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
103 hg_vfs.remove_file(lock_filename) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
104 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
105 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
106 /// Return whether the process that is/was holding the lock is known not to be |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
107 /// running anymore. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
108 fn lock_should_be_broken(data: &Option<String>) -> bool { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
109 (|| -> Option<bool> { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
110 let (prefix, pid) = data.as_ref()?.split_2(':')?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
111 if prefix != &*LOCK_PREFIX { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
112 return Some(false); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
113 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
114 let process_is_running; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
115 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
116 #[cfg(unix)] |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
117 { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
118 let pid: libc::pid_t = pid.parse().ok()?; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
119 unsafe { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
120 let signal = 0; // Test if we could send a signal, without sending |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
121 let result = libc::kill(pid, signal); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
122 if result == 0 { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
123 process_is_running = true |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
124 } else { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
125 let errno = |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
126 io::Error::last_os_error().raw_os_error().unwrap(); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
127 process_is_running = errno != libc::ESRCH |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
128 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
129 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
130 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
131 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
132 Some(!process_is_running) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
133 })() |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
134 .unwrap_or(false) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
135 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
136 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
137 lazy_static::lazy_static! { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
138 /// A string which is used to differentiate pid namespaces |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
139 /// |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
140 /// It's useful to detect "dead" processes and remove stale locks with |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
141 /// confidence. Typically it's just hostname. On modern linux, we include an |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
142 /// extra Linux-specific pid namespace identifier. |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
143 static ref LOCK_PREFIX: String = { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
144 // Note: this must match the behavior of `_getlockprefix` in `mercurial/lock.py` |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
145 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
146 /// Same as https://github.com/python/cpython/blob/v3.10.0/Modules/socketmodule.c#L5414 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
147 const BUFFER_SIZE: usize = 1024; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
148 let mut buffer = [0_i8; BUFFER_SIZE]; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
149 let hostname_bytes = unsafe { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
150 let result = libc::gethostname(buffer.as_mut_ptr(), BUFFER_SIZE); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
151 if result != 0 { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
152 panic!("gethostname: {}", io::Error::last_os_error()) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
153 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
154 std::ffi::CStr::from_ptr(buffer.as_mut_ptr()).to_bytes() |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
155 }; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
156 let hostname = |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
157 std::str::from_utf8(hostname_bytes).expect("non-UTF-8 hostname"); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
158 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
159 #[cfg(target_os = "linux")] |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
160 { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
161 use std::os::linux::fs::MetadataExt; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
162 match std::fs::metadata("/proc/self/ns/pid") { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
163 Ok(meta) => { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
164 return format!("{}/{:x}", hostname, meta.st_ino()) |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
165 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
166 Err(error) => { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
167 // TODO: match on `error.kind()` when `NotADirectory` |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
168 // is available on all supported Rust versions: |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
169 // https://github.com/rust-lang/rust/issues/86442 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
170 use libc::{ |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
171 ENOENT, // ErrorKind::NotFound |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
172 ENOTDIR, // ErrorKind::NotADirectory |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
173 EACCES, // ErrorKind::PermissionDenied |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
174 }; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
175 match error.raw_os_error() { |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
176 Some(ENOENT) | Some(ENOTDIR) | Some(EACCES) => {} |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
177 _ => panic!("stat /proc/self/ns/pid: {}", error), |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
178 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
179 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
180 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
181 } |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
182 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
183 hostname.to_owned() |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
184 }; |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
185 |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
186 static ref OUR_LOCK_DATA: String = format!("{}:{}", &*LOCK_PREFIX, std::process::id()); |
5734b03ecf3e
rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
187 } |