1 /*
2  * Copyright (C) 2015 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 #ifndef ANDROID_GATE_H
18 #define ANDROID_GATE_H
19 
20 #include <stdint.h>
21 #include <mutex>
22 
23 namespace android {
24 
25 // Gate is a synchronization object.
26 //
27 // Threads will pass if it is open.
28 // Threads will block (wait) if it is closed.
29 //
30 // When a gate is opened, all waiting threads will pass through.
31 //
32 // Since gate holds no external locks, consistency with external
33 // state needs to be handled elsewhere.
34 //
35 // We use mWaitCount to indicate the number of threads that have
36 // arrived at the gate via wait().  Each thread entering
37 // wait obtains a unique waitId (which is the current mWaitCount).
38 // This can be viewed as a sequence number.
39 //
40 // We use mPassCount to indicate the number of threads that have
41 // passed the gate.  If the waitId is less than or equal to the mPassCount
42 // then that thread has passed the gate.  An open gate sets mPassedCount
43 // to the current mWaitCount, allowing all prior threads to pass.
44 //
45 // See sync_timeline, sync_pt, etc. for graphics.
46 
47 class Gate {
48 public:
49     Gate(bool open = false) :
mOpen(open)50         mOpen(open),
51         mExit(false),
52         mWaitCount(0),
53         mPassCount(0)
54     { }
55 
56     // waits for the gate to open, returns immediately if gate is already open.
57     //
58     // Do not hold a monitor lock while calling this.
59     //
60     // returns true if we passed the gate normally
61     //         false if gate is terminated and we didn't pass the gate.
wait()62     bool wait() {
63         std::unique_lock<std::mutex> l(mLock);
64         size_t waitId = ++mWaitCount;
65         if (mOpen) {
66             mPassCount = waitId; // let me through
67         }
68         while (!passedGate_l(waitId) && !mExit) {
69             mCondition.wait(l);
70         }
71         return passedGate_l(waitId);
72     }
73 
74     // close the gate.
closeGate()75     void closeGate() {
76         std::lock_guard<std::mutex> l(mLock);
77         mOpen = false;
78         mExit = false;
79     }
80 
81     // open the gate.
82     // signal to all waiters it is okay to go.
openGate()83     void openGate() {
84         std::lock_guard<std::mutex> l(mLock);
85         mOpen = true;
86         mExit = false;
87         if (waiters_l() > 0) {
88             mPassCount = mWaitCount;  // allow waiting threads to go through
89             // unoptimized pthreads will wake thread to find we still hold lock.
90             mCondition.notify_all();
91         }
92     }
93 
94     // terminate (term has expired).
95     // all threads allowed to pass regardless of whether the gate is open or closed.
terminate()96     void terminate() {
97         std::lock_guard<std::mutex> l(mLock);
98         mExit = true;
99         if (waiters_l() > 0) {
100             // unoptimized pthreads will wake thread to find we still hold lock.
101             mCondition.notify_all();
102         }
103     }
104 
isOpen()105     bool isOpen() {
106         std::lock_guard<std::mutex> l(mLock);
107         return mOpen;
108     }
109 
110     // return how many waiters are at the gate.
waiters()111     size_t waiters() {
112         std::lock_guard<std::mutex> l(mLock);
113         return waiters_l();
114     }
115 
116 private:
117     bool                    mOpen;
118     bool                    mExit;
119     size_t                  mWaitCount;  // total number of threads that have called wait()
120     size_t                  mPassCount;  // total number of threads passed the gate.
121     std::condition_variable mCondition;
122     std::mutex              mLock;
123 
124     // return how many waiters are at the gate.
waiters_l()125     inline size_t waiters_l() {
126         return mWaitCount - mPassCount;
127     }
128 
129     // return whether the waitId (from mWaitCount) has passed through the gate
passedGate_l(size_t waitId)130     inline bool passedGate_l(size_t waitId) {
131         return (ssize_t)(waitId - mPassCount) <= 0;
132     }
133 };
134 
135 } // namespace android
136 
137 #endif // ANDROID_GATE_H
138