mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-26 11:38:51 +01:00
Run tunnels as singleton process (for a --cli-data-dir) (#177002)
* wip on singleton * wip * windows support * wip * wip * fix clippy
This commit is contained in:
125
cli/src/util/file_lock.rs
Normal file
125
cli/src/util/file_lock.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
use crate::util::errors::CodeError;
|
||||
use std::{fs::File, io};
|
||||
|
||||
pub struct FileLock {
|
||||
file: File,
|
||||
#[cfg(windows)]
|
||||
overlapped: winapi::um::minwinbase::OVERLAPPED,
|
||||
}
|
||||
|
||||
#[cfg(windows)] // overlapped is thread-safe, mark it so with this
|
||||
unsafe impl Send for FileLock {}
|
||||
|
||||
pub enum Lock {
|
||||
Acquired(FileLock),
|
||||
AlreadyLocked(File),
|
||||
}
|
||||
|
||||
/// Number of locked bytes in the file. On Windows, locking prevents reads,
|
||||
/// but consumers of the lock may still want to read what the locking file
|
||||
/// as written. Thus, only PREFIX_LOCKED_BYTES are locked, and any globally-
|
||||
/// readable content should be written after the prefix.
|
||||
#[cfg(windows)]
|
||||
pub const PREFIX_LOCKED_BYTES: usize = 1;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub const PREFIX_LOCKED_BYTES: usize = 0;
|
||||
|
||||
impl FileLock {
|
||||
#[cfg(windows)]
|
||||
pub fn acquire(file: File) -> Result<Lock, CodeError> {
|
||||
use std::os::windows::prelude::AsRawHandle;
|
||||
use winapi::{
|
||||
shared::winerror::{ERROR_IO_PENDING, ERROR_LOCK_VIOLATION},
|
||||
um::{
|
||||
fileapi::LockFileEx,
|
||||
minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY},
|
||||
},
|
||||
};
|
||||
|
||||
let handle = file.as_raw_handle();
|
||||
let (overlapped, ok) = unsafe {
|
||||
let mut overlapped = std::mem::zeroed();
|
||||
let ok = LockFileEx(
|
||||
handle,
|
||||
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
|
||||
0,
|
||||
PREFIX_LOCKED_BYTES as u32,
|
||||
0,
|
||||
&mut overlapped,
|
||||
);
|
||||
|
||||
(overlapped, ok)
|
||||
};
|
||||
|
||||
if ok != 0 {
|
||||
return Ok(Lock::Acquired(Self { file, overlapped }));
|
||||
}
|
||||
|
||||
let err = io::Error::last_os_error();
|
||||
let raw = err.raw_os_error();
|
||||
// docs report it should return ERROR_IO_PENDING, but in my testing it actually
|
||||
// returns ERROR_LOCK_VIOLATION. Or maybe winapi is wrong?
|
||||
if raw == Some(ERROR_IO_PENDING as i32) || raw == Some(ERROR_LOCK_VIOLATION as i32) {
|
||||
return Ok(Lock::AlreadyLocked(file));
|
||||
}
|
||||
|
||||
Err(CodeError::SingletonLockfileOpenFailed(err))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn acquire(file: File) -> Result<Lock, CodeError> {
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
let fd = file.as_raw_fd();
|
||||
let res = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
|
||||
if res == 0 {
|
||||
return Ok(Lock::Acquired(Self { file }));
|
||||
}
|
||||
|
||||
let err = io::Error::last_os_error();
|
||||
if err.kind() == io::ErrorKind::WouldBlock {
|
||||
return Ok(Lock::AlreadyLocked(file));
|
||||
}
|
||||
|
||||
Err(CodeError::SingletonLockfileOpenFailed(err))
|
||||
}
|
||||
|
||||
pub fn file(&self) -> &File {
|
||||
&self.file
|
||||
}
|
||||
|
||||
pub fn file_mut(&mut self) -> &mut File {
|
||||
&mut self.file
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileLock {
|
||||
#[cfg(windows)]
|
||||
fn drop(&mut self) {
|
||||
use std::os::windows::prelude::AsRawHandle;
|
||||
use winapi::um::fileapi::UnlockFileEx;
|
||||
|
||||
unsafe {
|
||||
UnlockFileEx(
|
||||
self.file.as_raw_handle(),
|
||||
0,
|
||||
u32::MAX,
|
||||
u32::MAX,
|
||||
&mut self.overlapped,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn drop(&mut self) {
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
unsafe { libc::flock(self.file.as_raw_fd(), libc::LOCK_UN) };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user