Commit db1c4352 authored by Loo Rong Jie's avatar Loo Rong Jie Committed by Commit Bot

[base] Use Win32 native condition variable

Switch to use Win32 CONDITION_VARIABLE like Chromium.

This greatly simplifies the implementation of ConditionVariable
class for Windows with better performance.

Bug:NO

Change-Id: Iea5e5cb80520a966aeb687bebb4b5256396cb13b
Reviewed-on: https://chromium-review.googlesource.com/519542Reviewed-by: 's avatarJochen Eisinger <jochen@chromium.org>
Commit-Queue: Loo Rong Jie <loorongjie@gmail.com>
Cr-Commit-Position: refs/heads/master@{#45651}
parent 9ad14ba1
...@@ -118,210 +118,44 @@ bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) { ...@@ -118,210 +118,44 @@ bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
#elif V8_OS_WIN #elif V8_OS_WIN
struct ConditionVariable::Event { ConditionVariable::ConditionVariable() {
Event() : handle_(::CreateEventA(NULL, true, false, NULL)) { InitializeConditionVariable(&native_handle_);
DCHECK(handle_ != NULL);
}
~Event() {
BOOL ok = ::CloseHandle(handle_);
DCHECK(ok);
USE(ok);
}
bool WaitFor(DWORD timeout_ms) {
DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
if (result == WAIT_OBJECT_0) {
return true;
}
DCHECK(result == WAIT_TIMEOUT);
return false;
}
HANDLE handle_;
Event* next_;
HANDLE thread_;
volatile bool notified_;
};
ConditionVariable::NativeHandle::~NativeHandle() {
DCHECK(waitlist_ == NULL);
while (freelist_ != NULL) {
Event* event = freelist_;
freelist_ = event->next_;
delete event;
}
}
ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
LockGuard<Mutex> lock_guard(&mutex_);
// Grab an event from the free list or create a new one.
Event* event = freelist_;
if (event != NULL) {
freelist_ = event->next_;
} else {
event = new Event;
}
event->thread_ = GetCurrentThread();
event->notified_ = false;
#ifdef DEBUG
// The event must not be on the wait list.
for (Event* we = waitlist_; we != NULL; we = we->next_) {
DCHECK_NE(event, we);
}
#endif
// Prepend the event to the wait list.
event->next_ = waitlist_;
waitlist_ = event;
return event;
}
void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
LockGuard<Mutex> lock_guard(&mutex_);
// Remove the event from the wait list.
for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
DCHECK(*wep);
if (*wep == event) {
*wep = event->next_;
break;
}
}
#ifdef DEBUG
// The event must not be on the free list.
for (Event* fe = freelist_; fe != NULL; fe = fe->next_) {
DCHECK_NE(event, fe);
}
#endif
// Reset the event.
BOOL ok = ::ResetEvent(event->handle_);
DCHECK(ok);
USE(ok);
// Insert the event into the free list.
event->next_ = freelist_;
freelist_ = event;
// Forward signals delivered after the timeout to the next waiting event.
if (!result && event->notified_ && waitlist_ != NULL) {
ok = ::SetEvent(waitlist_->handle_);
DCHECK(ok);
USE(ok);
waitlist_->notified_ = true;
}
} }
ConditionVariable::ConditionVariable() {}
ConditionVariable::~ConditionVariable() {} ConditionVariable::~ConditionVariable() {}
void ConditionVariable::NotifyOne() { WakeConditionVariable(&native_handle_); }
void ConditionVariable::NotifyOne() {
// Notify the thread with the highest priority in the waitlist
// that was not already signalled.
LockGuard<Mutex> lock_guard(native_handle_.mutex());
Event* highest_event = NULL;
int highest_priority = std::numeric_limits<int>::min();
for (Event* event = native_handle().waitlist();
event != NULL;
event = event->next_) {
if (event->notified_) {
continue;
}
int priority = GetThreadPriority(event->thread_);
DCHECK_NE(THREAD_PRIORITY_ERROR_RETURN, priority);
if (priority >= highest_priority) {
highest_priority = priority;
highest_event = event;
}
}
if (highest_event != NULL) {
DCHECK(!highest_event->notified_);
::SetEvent(highest_event->handle_);
highest_event->notified_ = true;
}
}
void ConditionVariable::NotifyAll() { void ConditionVariable::NotifyAll() {
// Notify all threads on the waitlist. WakeAllConditionVariable(&native_handle_);
LockGuard<Mutex> lock_guard(native_handle_.mutex());
for (Event* event = native_handle().waitlist();
event != NULL;
event = event->next_) {
if (!event->notified_) {
::SetEvent(event->handle_);
event->notified_ = true;
}
}
} }
void ConditionVariable::Wait(Mutex* mutex) { void ConditionVariable::Wait(Mutex* mutex) {
// Create and setup the wait event. mutex->AssertHeldAndUnmark();
Event* event = native_handle_.Pre(); SleepConditionVariableCS(&native_handle_, &mutex->native_handle(), INFINITE);
mutex->AssertUnheldAndMark();
// Release the user mutex.
mutex->Unlock();
// Wait on the wait event.
while (!event->WaitFor(INFINITE)) {
}
// Reaquire the user mutex.
mutex->Lock();
// Release the wait event (we must have been notified).
DCHECK(event->notified_);
native_handle_.Post(event, true);
} }
bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) { bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
// Create and setup the wait event. int64_t msec = rel_time.InMilliseconds();
Event* event = native_handle_.Pre(); mutex->AssertHeldAndUnmark();
BOOL result = SleepConditionVariableCS(
// Release the user mutex. &native_handle_, &mutex->native_handle(), static_cast<DWORD>(msec));
mutex->Unlock(); #ifdef DEBUG
if (!result) {
// Wait on the wait event. // On failure, we only expect the CV to timeout. Any other error value means
TimeTicks now = TimeTicks::Now(); // that we've unexpectedly woken up.
TimeTicks end = now + rel_time; // Note that WAIT_TIMEOUT != ERROR_TIMEOUT. WAIT_TIMEOUT is used with the
bool result = false; // WaitFor* family of functions as a direct return value. ERROR_TIMEOUT is
while (true) { // used with GetLastError().
int64_t msec = (end - now).InMilliseconds(); DCHECK_EQ(static_cast<DWORD>(ERROR_TIMEOUT), GetLastError());
if (msec >= static_cast<int64_t>(INFINITE)) {
result = event->WaitFor(INFINITE - 1);
if (result) {
break;
}
now = TimeTicks::Now();
} else {
result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
break;
}
} }
#endif
// Reaquire the user mutex. mutex->AssertUnheldAndMark();
mutex->Lock(); return result != 0;
// Release the wait event.
DCHECK(!result || event->notified_);
native_handle_.Post(event, result);
return result;
} }
#endif // V8_OS_POSIX #endif // V8_OS_POSIX
......
...@@ -63,25 +63,7 @@ class V8_BASE_EXPORT ConditionVariable final { ...@@ -63,25 +63,7 @@ class V8_BASE_EXPORT ConditionVariable final {
#if V8_OS_POSIX #if V8_OS_POSIX
typedef pthread_cond_t NativeHandle; typedef pthread_cond_t NativeHandle;
#elif V8_OS_WIN #elif V8_OS_WIN
struct Event; typedef CONDITION_VARIABLE NativeHandle;
class NativeHandle final {
public:
NativeHandle() : waitlist_(NULL), freelist_(NULL) {}
~NativeHandle();
Event* Pre() WARN_UNUSED_RESULT;
void Post(Event* event, bool result);
Mutex* mutex() { return &mutex_; }
Event* waitlist() { return waitlist_; }
private:
Event* waitlist_;
Event* freelist_;
Mutex mutex_;
DISALLOW_COPY_AND_ASSIGN(NativeHandle);
};
#endif #endif
NativeHandle& native_handle() { NativeHandle& native_handle() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment