• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <chrono>
20 #include <functional>
21 #include <mutex>
22 #include <unordered_set>
23 
24 #include <gtest/gtest_prod.h>
25 
26 #include "android-base/macros.h"
27 
28 namespace android {
29 
30 /*
31  * AtomicState manages updating or waiting on a state enum between multiple threads.
32  */
33 template <typename T>
34 class AtomicState {
35  public:
AtomicState(T state)36   explicit AtomicState(T state) : state_(state) {}
37   ~AtomicState() = default;
38 
39   /*
40    * Set the state to `to`.  Wakes up any waiters that are waiting on the new state.
41    */
set(T to)42   void set(T to) {
43     std::lock_guard<std::mutex> lock(m_);
44     state_ = to;
45     cv_.notify_all();
46   }
47 
48   /*
49    * If the state is `from`, change it to `to` and return true.  Otherwise don't change
50    * it and return false.  If the state is changed, wakes up any waiters that are waiting
51    * on the new state.
52    */
transition(T from,T to)53   bool transition(T from, T to) {
54     return transition_or(from, to, [&] { return state_; });
55   }
56 
57   /*
58    * If the state is `from`, change it to `to` and return true.  Otherwise, call `or_func`,
59    * set the state to the value it returns and return false.  Wakes up any waiters that are
60    * waiting on the new state.
61    */
transition_or(T from,T to,const std::function<T ()> & orFunc)62   bool transition_or(T from, T to, const std::function<T()>& orFunc) {
63     std::lock_guard<std::mutex> lock(m_);
64 
65     bool failed = false;
66     if (state_ == from) {
67       state_ = to;
68     } else {
69       failed = true;
70       state_ = orFunc();
71     }
72     cv_.notify_all();
73 
74     return !failed;
75   }
76 
77   /*
78    * Block until the state is either `state1` or `state2`, or the time limit is reached.
79    * Returns true if the time limit was not reached, false if it was reached.
80    */
wait_for_either_of(T state1,T state2,std::chrono::milliseconds ms)81   bool wait_for_either_of(T state1, T state2, std::chrono::milliseconds ms) {
82     std::unique_lock<std::mutex> lock(m_);
83     bool success = cv_.wait_for(lock, ms, [&] { return state_ == state1 || state_ == state2; });
84     return success;
85   }
86 
87  private:
88   T state_;
89   std::mutex m_;
90   std::condition_variable cv_;
91 
92   FRIEND_TEST(AtomicStateTest, transition);
93   FRIEND_TEST(AtomicStateTest, wait);
94 
95   DISALLOW_COPY_AND_ASSIGN(AtomicState);
96 };
97 
98 }  // namespace android
99