blob: 365eab987e39c63420e711b49d25c5f23b5fe369 [file] [log] [blame]
Colin Cross0b1e8d32022-05-11 15:17:55 -07001/*
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
28namespace android {
29
30/*
31 * AtomicState manages updating or waiting on a state enum between multiple threads.
32 */
33template <typename T>
34class AtomicState {
35 public:
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 */
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 */
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 */
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 */
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