blob: a265f605ecea1c4aa454173359444329ea6604fb [file] [log] [blame]
Andrew Walbrand0ef4002022-05-16 16:14:10 +00001// 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
15use 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)]
22pub struct Monitor<T> {
23 pub state: Mutex<T>,
24 pub cv: Condvar,
25}
26
27impl<T> Monitor<T> {
28 /// Creates a new mutex wrapping the given value, and a new condition variable to go with it.
29 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.
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.
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
57fn convert_poison_error<T>(err: PoisonError<(T, WaitTimeoutResult)>) -> PoisonError<T> {
58 PoisonError::new(err.into_inner().0)
59}