xref: /aosp_15_r20/external/crosvm/base/src/sys/windows/multi_process_mutex.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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