Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Unified Diff: runtime/platform/thread_win.cc

Issue 9424050: Reimplement Windows Monitors. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Avoid copying by holding internal lock during SetEvent Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/platform/thread_win.h ('k') | runtime/tests/vm/vm.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/platform/thread_win.cc
diff --git a/runtime/platform/thread_win.cc b/runtime/platform/thread_win.cc
index 47acd91c74b974274dbffe1567744db49e6c0aa1..d4ddab0cadbfd64eaff327793ed90fabe6e4eb84 100644
--- a/runtime/platform/thread_win.cc
+++ b/runtime/platform/thread_win.cc
@@ -136,27 +136,20 @@ void Mutex::Unlock() {
}
+ThreadLocalKey MonitorWaitData::monitor_wait_data_key_ =
+ Thread::kUnsetThreadLocalKey;
+
+
Monitor::Monitor() {
InitializeCriticalSection(&data_.cs_);
- // Create auto-reset event used to implement Notify. Auto-reset
- // events only wake one thread waiting for them on SetEvent.
- data_.notify_event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
- // Create manual-reset event used to implement
- // NotifyAll. Manual-reset events wake all threads waiting for them
- // on SetEvent.
- data_.notify_all_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
- if ((data_.notify_event_ == NULL) || (data_.notify_all_event_ == NULL)) {
- FATAL("Failed allocating event object for monitor");
- }
InitializeCriticalSection(&data_.waiters_cs_);
- data_.waiters_ = 0;
+ data_.waiters_head_ = NULL;
+ data_.waiters_tail_ = NULL;
}
Monitor::~Monitor() {
DeleteCriticalSection(&data_.cs_);
- CloseHandle(data_.notify_event_);
- CloseHandle(data_.notify_all_event_);
DeleteCriticalSection(&data_.waiters_cs_);
}
@@ -171,57 +164,150 @@ void Monitor::Exit() {
}
+void MonitorData::AddWaiter(MonitorWaitData* wait_data) {
+ // Add the MonitorWaitData object to the list of objects waiting for
+ // this monitor.
+ EnterCriticalSection(&waiters_cs_);
+ if (waiters_tail_ == NULL) {
+ ASSERT(waiters_head_ == NULL);
+ waiters_head_ = waiters_tail_ = wait_data;
+ } else {
+ waiters_tail_->next_ = wait_data;
+ waiters_tail_ = wait_data;
+ }
+ LeaveCriticalSection(&waiters_cs_);
+}
+
+
+void MonitorData::RemoveWaiter(MonitorWaitData* wait_data) {
+ // Remove the MonitorWaitData object from the list of objects
+ // waiting for this monitor.
+ EnterCriticalSection(&waiters_cs_);
+ MonitorWaitData* previous = NULL;
+ MonitorWaitData* current = waiters_head_;
+ while (current != NULL) {
+ if (current == wait_data) {
+ if (waiters_head_ == waiters_tail_) {
+ waiters_head_ = waiters_tail_ = NULL;
+ } else if (current == waiters_head_) {
+ waiters_head_ = waiters_head_->next_;
+ } else if (current == waiters_tail_) {
+ ASSERT(previous != NULL);
+ waiters_tail_ = previous;
+ previous->next_ = NULL;
+ } else {
+ ASSERT(previous != NULL);
+ previous->next_ = current->next_;
+ }
+ break;
+ }
+ previous = current;
+ current = current->next_;
+ }
+ LeaveCriticalSection(&waiters_cs_);
+}
+
+
+void MonitorData::SignalAndRemoveFirstWaiter() {
+ EnterCriticalSection(&waiters_cs_);
+ MonitorWaitData* first = waiters_head_;
+ if (first != NULL) {
+ // Remove from list.
+ if (waiters_head_ == waiters_tail_) {
+ waiters_tail_ = waiters_head_ = NULL;
+ } else {
+ waiters_head_ = waiters_head_->next_;
+ }
+ // Signal event.
+ BOOL result = SetEvent(first->event_);
+ if (result == 0) {
+ FATAL("Monitor::Notify failed to signal event");
+ }
+ }
+ LeaveCriticalSection(&waiters_cs_);
+}
+
+
+void MonitorData::SignalAndRemoveAllWaiters() {
+ EnterCriticalSection(&waiters_cs_);
+ // Extract list to signal.
+ MonitorWaitData* current = waiters_head_;
+ // Clear list.
+ waiters_head_ = waiters_tail_ = NULL;
+ // Iterate and signal all events.
+ while (current != NULL) {
+ BOOL result = SetEvent(current->event_);
+ if (result == 0) {
+ FATAL("Failed to set event for NotifyAll");
+ }
+ current = current->next_;
+ }
+ LeaveCriticalSection(&waiters_cs_);
+}
+
+
+MonitorWaitData* MonitorData::GetMonitorWaitDataForThread() {
+ // Ensure that the thread local key for monitor wait data objects is
+ // initialized.
+ EnterCriticalSection(&waiters_cs_);
+ if (MonitorWaitData::monitor_wait_data_key_ == Thread::kUnsetThreadLocalKey) {
+ MonitorWaitData::monitor_wait_data_key_ = Thread::CreateThreadLocal();
+ }
+ LeaveCriticalSection(&waiters_cs_);
+
+ // Get the MonitorWaitData object containing the event for this
+ // thread from thread local storage. Create it if it does not exist.
+ uword raw_wait_data =
+ Thread::GetThreadLocal(MonitorWaitData::monitor_wait_data_key_);
+ MonitorWaitData* wait_data = NULL;
+ if (raw_wait_data == 0) {
+ HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ wait_data = new MonitorWaitData(event);
+ Thread::SetThreadLocal(MonitorWaitData::monitor_wait_data_key_,
+ reinterpret_cast<uword>(wait_data));
+ } else {
+ wait_data = reinterpret_cast<MonitorWaitData*>(raw_wait_data);
+ wait_data->next_ = NULL;
+ }
+ return wait_data;
+}
+
+
Monitor::WaitResult Monitor::Wait(int64_t millis) {
Monitor::WaitResult retval = kNotified;
- // Record the fact that we will start waiting. This is used to only
- // reset the notify all event when all waiting threads have dealt
- // with the event.
- EnterCriticalSection(&data_.waiters_cs_);
- data_.waiters_++;
- LeaveCriticalSection(&data_.waiters_cs_);
+ // Get the wait data object containing the event to wait for.
+ MonitorWaitData* wait_data = data_.GetMonitorWaitDataForThread();
+
+ // Start waiting by adding the MonitorWaitData to the list of
+ // waiters.
+ data_.AddWaiter(wait_data);
// Leave the monitor critical section while waiting.
LeaveCriticalSection(&data_.cs_);
- // Perform the actual wait using wait for multiple objects on both
- // the notify and the notify all events.
- static const intptr_t kNotifyEventIndex = 0;
- static const intptr_t kNotifyAllEventIndex = 1;
- static const intptr_t kNumberOfEvents = 2;
- HANDLE events[kNumberOfEvents];
- events[kNotifyEventIndex] = data_.notify_event_;
- events[kNotifyAllEventIndex] = data_.notify_all_event_;
-
+ // Perform the actual wait on the event.
DWORD result = WAIT_FAILED;
if (millis == 0) {
// Wait forever for a Notify or a NotifyAll event.
- result = WaitForMultipleObjects(2, events, FALSE, INFINITE);
+ result = WaitForSingleObject(wait_data->event_, INFINITE);
if (result == WAIT_FAILED) {
FATAL("Monitor::Wait failed");
}
} else {
// Wait for the given period of time for a Notify or a NotifyAll
// event.
- result = WaitForMultipleObjects(2, events, FALSE, millis);
+ result = WaitForSingleObject(wait_data->event_, millis);
if (result == WAIT_FAILED) {
FATAL("Monitor::Wait with timeout failed");
}
if (result == WAIT_TIMEOUT) {
+ // No longer waiting. Remove from the list of waiters.
+ data_.RemoveWaiter(wait_data);
retval = kTimedOut;
}
}
- // Check if we are the last waiter on a notify all. If we are, reset
- // the notify all event.
- EnterCriticalSection(&data_.waiters_cs_);
- data_.waiters_--;
- if ((data_.waiters_ == 0) &&
- (result == (WAIT_OBJECT_0 + kNotifyAllEventIndex))) {
- ResetEvent(data_.notify_all_event_);
- }
- LeaveCriticalSection(&data_.waiters_cs_);
-
// Reacquire the monitor critical section before continuing.
EnterCriticalSection(&data_.cs_);
@@ -230,24 +316,17 @@ Monitor::WaitResult Monitor::Wait(int64_t millis) {
void Monitor::Notify() {
- // Signal one waiter through the notify auto-reset event if there
- // are any waiters.
- EnterCriticalSection(&data_.waiters_cs_);
- if (data_.waiters_ > 0) {
- SetEvent(data_.notify_event_);
- }
- LeaveCriticalSection(&data_.waiters_cs_);
+ data_.SignalAndRemoveFirstWaiter();
}
void Monitor::NotifyAll() {
- // Signal all waiters through the notify all manual-reset event if
- // there are any waiters.
- EnterCriticalSection(&data_.waiters_cs_);
- if (data_.waiters_ > 0) {
- SetEvent(data_.notify_all_event_);
- }
- LeaveCriticalSection(&data_.waiters_cs_);
+ // If one of the objects in the list of waiters wakes because of a
+ // timeout before we signal it, that object will get an extra
+ // signal. This will be treated as a spurious wake-up and is OK
+ // since all uses of monitors should recheck the condition after a
+ // Wait.
+ data_.SignalAndRemoveAllWaiters();
}
} // namespace dart
« no previous file with comments | « runtime/platform/thread_win.h ('k') | runtime/tests/vm/vm.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698