// Copyright (C) 2014 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. #include "emugl/common/condition_variable.h" #include "emugl/common/lazy_instance.h" #include "emugl/common/mutex.h" #include "emugl/common/pod_vector.h" // Technical note: this is loosely based on the Chromium implementation // of ConditionVariable. This version works on Windows XP and above and // doesn't try to use Vista's CONDITION_VARIABLE types. namespace emugl { namespace { // Helper class which implements a free list of event handles. class WaitEventStorage { public: WaitEventStorage() : mFreeHandles(), mLock() {} ~WaitEventStorage() { for (size_t n = 0; n < mFreeHandles.size(); ++n) { CloseHandle(mFreeHandles[n]); } } HANDLE alloc() { HANDLE handle; mLock.lock(); size_t size = mFreeHandles.size(); if (size > 0) { handle = mFreeHandles[size - 1U]; mFreeHandles.remove(size - 1U); } else { handle = CreateEvent(NULL, TRUE, FALSE, NULL); } mLock.unlock(); return handle; } void free(HANDLE h) { mLock.lock(); ResetEvent(h); mFreeHandles.push_back(h); mLock.unlock(); } private: PodVector mFreeHandles; Mutex mLock; }; LazyInstance sWaitEvents = LAZY_INSTANCE_INIT; } // namespace ConditionVariable::ConditionVariable() : mWaiters(), mLock() {} ConditionVariable::~ConditionVariable() { mLock.lock(); for (size_t n = 0; n < mWaiters.size(); ++n) { CloseHandle(mWaiters[n]); } mWaiters.resize(0U); mLock.unlock(); } void ConditionVariable::wait(Mutex* userLock) { // Grab new waiter event handle. mLock.lock(); HANDLE handle = sWaitEvents->alloc(); mWaiters.push_back(handle); mLock.unlock(); // Unlock user lock then wait for event. userLock->unlock(); WaitForSingleObject(handle, INFINITE); // NOTE: The handle has been removed from mWaiters here, // see signal() below. Close/recycle the event. sWaitEvents->free(handle); userLock->lock(); } void ConditionVariable::signal() { mLock.lock(); size_t size = mWaiters.size(); if (size > 0U) { // NOTE: This wakes up the thread that went to sleep most // recently (LIFO) for performance reason. For better // fairness, using (FIFO) would be appropriate. HANDLE handle = mWaiters[size - 1U]; mWaiters.remove(size - 1U); SetEvent(handle); // NOTE: The handle will be closed/recycled by the waiter. } else { // Nothing to signal. } mLock.unlock(); } } // namespace emugl