Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/cli/src/util/file_lock.rs
3316 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
use crate::util::errors::CodeError;
7
use std::{fs::File, io};
8
9
pub struct FileLock {
10
file: File,
11
#[cfg(windows)]
12
overlapped: winapi::um::minwinbase::OVERLAPPED,
13
}
14
15
#[cfg(windows)] // overlapped is thread-safe, mark it so with this
16
unsafe impl Send for FileLock {}
17
18
pub enum Lock {
19
Acquired(FileLock),
20
AlreadyLocked(File),
21
}
22
23
/// Number of locked bytes in the file. On Windows, locking prevents reads,
24
/// but consumers of the lock may still want to read what the locking file
25
/// as written. Thus, only PREFIX_LOCKED_BYTES are locked, and any globally-
26
/// readable content should be written after the prefix.
27
#[cfg(windows)]
28
pub const PREFIX_LOCKED_BYTES: usize = 1;
29
30
#[cfg(unix)]
31
pub const PREFIX_LOCKED_BYTES: usize = 0;
32
33
impl 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
// docs report it should return ERROR_IO_PENDING, but in my testing it actually
67
// returns ERROR_LOCK_VIOLATION. Or maybe winapi is wrong?
68
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
102
impl 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
}
126
127