cli/util/
app_lock.rs

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#[cfg(windows)]
7use std::{io, ptr};
8
9#[cfg(windows)]
10use winapi::{
11	shared::winerror::ERROR_ALREADY_EXISTS,
12	um::{handleapi::CloseHandle, synchapi::CreateMutexA, winnt::HANDLE},
13};
14
15use super::errors::CodeError;
16
17pub struct AppMutex {
18	#[cfg(windows)]
19	handle: HANDLE,
20}
21
22#[cfg(windows)] // handle is thread-safe, mark it so with this
23unsafe impl Send for AppMutex {}
24
25impl AppMutex {
26	#[cfg(unix)]
27	pub fn new(_name: &str) -> Result<Self, CodeError> {
28		Ok(Self {})
29	}
30
31	#[cfg(windows)]
32	pub fn new(name: &str) -> Result<Self, CodeError> {
33		use std::ffi::CString;
34
35		let cname = CString::new(name).unwrap();
36		let handle = unsafe { CreateMutexA(ptr::null_mut(), 0, cname.as_ptr() as _) };
37
38		if !handle.is_null() {
39			return Ok(Self { handle });
40		}
41
42		let err = io::Error::last_os_error();
43		let raw = err.raw_os_error();
44		// docs report it should return ERROR_IO_PENDING, but in my testing it actually
45		// returns ERROR_LOCK_VIOLATION. Or maybe winapi is wrong?
46		if raw == Some(ERROR_ALREADY_EXISTS as i32) {
47			return Err(CodeError::AppAlreadyLocked(name.to_string()));
48		}
49
50		Err(CodeError::AppLockFailed(err))
51	}
52}
53
54impl Drop for AppMutex {
55	fn drop(&mut self) {
56		#[cfg(windows)]
57		unsafe {
58			CloseHandle(self.handle)
59		};
60	}
61}