1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::ptr::null_mut; 6 7 use serde::Deserialize; 8 use serde::Serialize; 9 use winapi::shared::winerror::WAIT_TIMEOUT; 10 use winapi::um::handleapi::INVALID_HANDLE_VALUE; 11 use winapi::um::synchapi::CreateMutexA; 12 use winapi::um::synchapi::ReleaseMutex; 13 use winapi::um::synchapi::WaitForSingleObject; 14 use winapi::um::winbase::INFINITE; 15 use winapi::um::winbase::WAIT_ABANDONED; 16 use winapi::um::winbase::WAIT_OBJECT_0; 17 18 use super::Error; 19 use super::Result; 20 use crate::descriptor::AsRawDescriptor; 21 use crate::descriptor::FromRawDescriptor; 22 use crate::descriptor::SafeDescriptor; 23 24 /// A Mutex (no data) that works across processes on Windows. 25 #[derive(Serialize, Deserialize, Debug)] 26 pub struct MultiProcessMutex { 27 lock: SafeDescriptor, 28 } 29 30 impl MultiProcessMutex { new() -> Result<Self>31 pub fn new() -> Result<Self> { 32 // SAFETY: Trivially safe (no memory passed, error checked). 33 // 34 // Note that we intentionally make this handle uninheritable by default via the mutex attrs. 35 let lock_handle = unsafe { 36 CreateMutexA( 37 /* lpMutexAttributes= */ null_mut(), 38 false as i32, 39 null_mut(), 40 ) 41 }; 42 43 if lock_handle == INVALID_HANDLE_VALUE { 44 Err(Error::last()) 45 } else { 46 Ok(Self { 47 // SAFETY: 48 // Safe because the handle is valid & we own it exclusively. 49 lock: unsafe { SafeDescriptor::from_raw_descriptor(lock_handle) }, 50 }) 51 } 52 } 53 54 /// Locks the mutex, returning a RAII guard similar to std::sync::Mutex. lock(&self) -> MultiProcessMutexGuard55 pub fn lock(&self) -> MultiProcessMutexGuard { 56 if let Some(guard) = self.try_lock(INFINITE) { 57 guard 58 } else { 59 // This should *never* happen. 60 panic!("Timed out locking mutex with an infinite timeout. This should never happen."); 61 } 62 } 63 64 /// Tries to lock the mutex, returning a RAII guard similar to std::sync::Mutex if we obtained 65 /// the lock within the timeout. try_lock(&self, timeout_ms: u32) -> Option<MultiProcessMutexGuard>66 pub fn try_lock(&self, timeout_ms: u32) -> Option<MultiProcessMutexGuard> { 67 // SAFETY: 68 // Safe because the mutex handle is guaranteed to exist. 69 match unsafe { WaitForSingleObject(self.lock.as_raw_descriptor(), timeout_ms) } { 70 WAIT_OBJECT_0 => Some(MultiProcessMutexGuard { lock: &self.lock }), 71 WAIT_TIMEOUT => None, 72 WAIT_ABANDONED => panic!( 73 "The thread holding the mutex exited without releasing the mutex.\ 74 Protected data may be corrupt." 75 ), 76 _ => { 77 // This should *never* happen. 78 panic!("Failed to lock mutex {:?}", Error::last()) 79 } 80 } 81 } 82 83 /// Creates a new reference to the mutex. try_clone(&self) -> Result<Self>84 pub fn try_clone(&self) -> Result<Self> { 85 Ok(Self { 86 lock: self.lock.try_clone()?, 87 }) 88 } 89 } 90 91 /// RAII guard for MultiProcessMutex. 92 pub struct MultiProcessMutexGuard<'a> { 93 lock: &'a SafeDescriptor, 94 } 95 96 impl<'a> Drop for MultiProcessMutexGuard<'a> { drop(&mut self)97 fn drop(&mut self) { 98 // SAFETY: We own the descriptor and is expected to be valid. 99 if unsafe { ReleaseMutex(self.lock.as_raw_descriptor()) } == 0 { 100 panic!("Failed to unlock mutex: {:?}.", Error::last()) 101 } 102 } 103 } 104