1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::{
16     sync::{Condvar, LockResult, Mutex, MutexGuard, PoisonError, WaitTimeoutResult},
17     time::Duration,
18 };
19 
20 /// A mutex with an associated condition variable.
21 #[derive(Debug)]
22 pub struct Monitor<T> {
23     pub state: Mutex<T>,
24     pub cv: Condvar,
25 }
26 
27 impl<T> Monitor<T> {
28     /// Creates a new mutex wrapping the given value, and a new condition variable to go with it.
new(state: T) -> Self29     pub fn new(state: T) -> Self {
30         Self { state: Mutex::new(state), cv: Condvar::default() }
31     }
32 
33     /// Waits on the condition variable while the given condition holds true on the contents of the
34     /// mutex.
35     ///
36     /// Blocks until the condition variable is notified and the function returns false.
wait_while(&self, condition: impl FnMut(&mut T) -> bool) -> LockResult<MutexGuard<T>>37     pub fn wait_while(&self, condition: impl FnMut(&mut T) -> bool) -> LockResult<MutexGuard<T>> {
38         self.cv.wait_while(self.state.lock()?, condition)
39     }
40 
41     /// Waits on the condition variable while the given condition holds true on the contents of the
42     /// mutex, with a timeout.
43     ///
44     /// Blocks until the condition variable is notified and the function returns false, or the
45     /// timeout elapses.
wait_timeout_while( &self, timeout: Duration, condition: impl FnMut(&mut T) -> bool, ) -> Result<(MutexGuard<T>, WaitTimeoutResult), PoisonError<MutexGuard<T>>>46     pub fn wait_timeout_while(
47         &self,
48         timeout: Duration,
49         condition: impl FnMut(&mut T) -> bool,
50     ) -> Result<(MutexGuard<T>, WaitTimeoutResult), PoisonError<MutexGuard<T>>> {
51         self.cv
52             .wait_timeout_while(self.state.lock()?, timeout, condition)
53             .map_err(convert_poison_error)
54     }
55 }
56 
convert_poison_error<T>(err: PoisonError<(T, WaitTimeoutResult)>) -> PoisonError<T>57 fn convert_poison_error<T>(err: PoisonError<(T, WaitTimeoutResult)>) -> PoisonError<T> {
58     PoisonError::new(err.into_inner().0)
59 }
60