blob: dfc15b78878e214cb93bb0b0488d4db651c0ec8d [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_GATE_H
#define ANDROID_GATE_H
#include <stdint.h>
#include <mutex>
namespace android {
// Gate is a synchronization object.
//
// Threads will pass if it is open.
// Threads will block (wait) if it is closed.
//
// When a gate is opened, all waiting threads will pass through.
//
// Since gate holds no external locks, consistency with external
// state needs to be handled elsewhere.
//
// We use mWaitCount to indicate the number of threads that have
// arrived at the gate via wait(). Each thread entering
// wait obtains a unique waitId (which is the current mWaitCount).
// This can be viewed as a sequence number.
//
// We use mPassCount to indicate the number of threads that have
// passed the gate. If the waitId is less than or equal to the mPassCount
// then that thread has passed the gate. An open gate sets mPassedCount
// to the current mWaitCount, allowing all prior threads to pass.
//
// See sync_timeline, sync_pt, etc. for graphics.
class Gate {
public:
Gate(bool open = false) :
mOpen(open),
mExit(false),
mWaitCount(0),
mPassCount(0)
{ }
// waits for the gate to open, returns immediately if gate is already open.
//
// Do not hold a monitor lock while calling this.
//
// returns true if we passed the gate normally
// false if gate is terminated and we didn't pass the gate.
bool wait() {
std::unique_lock<std::mutex> l(mLock);
size_t waitId = ++mWaitCount;
if (mOpen) {
mPassCount = waitId; // let me through
}
while (!passedGate_l(waitId) && !mExit) {
mCondition.wait(l);
}
return passedGate_l(waitId);
}
// close the gate.
void closeGate() {
std::lock_guard<std::mutex> l(mLock);
mOpen = false;
mExit = false;
}
// open the gate.
// signal to all waiters it is okay to go.
void openGate() {
std::lock_guard<std::mutex> l(mLock);
mOpen = true;
mExit = false;
if (waiters_l() > 0) {
mPassCount = mWaitCount; // allow waiting threads to go through
// unoptimized pthreads will wake thread to find we still hold lock.
mCondition.notify_all();
}
}
// terminate (term has expired).
// all threads allowed to pass regardless of whether the gate is open or closed.
void terminate() {
std::lock_guard<std::mutex> l(mLock);
mExit = true;
if (waiters_l() > 0) {
// unoptimized pthreads will wake thread to find we still hold lock.
mCondition.notify_all();
}
}
bool isOpen() {
std::lock_guard<std::mutex> l(mLock);
return mOpen;
}
// return how many waiters are at the gate.
size_t waiters() {
std::lock_guard<std::mutex> l(mLock);
return waiters_l();
}
private:
bool mOpen;
bool mExit;
size_t mWaitCount; // total number of threads that have called wait()
size_t mPassCount; // total number of threads passed the gate.
std::condition_variable mCondition;
std::mutex mLock;
// return how many waiters are at the gate.
inline size_t waiters_l() {
return mWaitCount - mPassCount;
}
// return whether the waitId (from mWaitCount) has passed through the gate
inline bool passedGate_l(size_t waitId) {
return (ssize_t)(waitId - mPassCount) <= 0;
}
};
} // namespace android
#endif // ANDROID_GATE_H