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

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: Fix timeout handling. 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
Index: runtime/platform/thread_win.cc
diff --git a/runtime/platform/thread_win.cc b/runtime/platform/thread_win.cc
index 47acd91c74b974274dbffe1567744db49e6c0aa1..4d3b8046856426332df0ae47b7b900f767691d37 100644
--- a/runtime/platform/thread_win.cc
+++ b/runtime/platform/thread_win.cc
@@ -136,27 +136,21 @@ 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_head_ = NULL;
+ data_.waiters_tail_ = NULL;
data_.waiters_ = 0;
}
Monitor::~Monitor() {
DeleteCriticalSection(&data_.cs_);
- CloseHandle(data_.notify_event_);
- CloseHandle(data_.notify_all_event_);
DeleteCriticalSection(&data_.waiters_cs_);
}
@@ -171,57 +165,149 @@ 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) {
Søren Gjesse 2012/02/21 16:00:48 ASSERT(waiters_head_ == NULL)
Mads Ager (google) 2012/02/22 12:32:51 Done.
+ waiters_head_ = waiters_tail_ = wait_data;
+ } else {
+ waiters_tail_->next_ = wait_data;
+ waiters_tail_ = wait_data;
+ }
+ waiters_++;
+ 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_;
+ }
+ waiters_--;
+ break;
+ }
+ previous = current;
+ current = current->next_;
+ }
+ LeaveCriticalSection(&waiters_cs_);
+}
+
+
+MonitorWaitData* MonitorData::RemoveFirstWaiter() {
+ EnterCriticalSection(&waiters_cs_);
+ MonitorWaitData* first = NULL;
+ if (waiters_ > 0) {
+ first = waiters_head_;
+ if (waiters_ == 1) {
+ waiters_tail_ = waiters_head_ = NULL;
+ } else {
+ waiters_head_ = waiters_head_->next_;
+ }
+ waiters_--;
+ }
+ LeaveCriticalSection(&waiters_cs_);
+ return first;
+}
+
+
+MonitorWaitData** MonitorData::RemoveAllWaiters() {
+ EnterCriticalSection(&waiters_cs_);
+ MonitorWaitData** result =
+ reinterpret_cast<MonitorWaitData**>(calloc(waiters_ + 1, kWordSize));
+ MonitorWaitData* current = waiters_head_;
+ intptr_t i = 0;
+ while (current != NULL) {
+ result[i++] = current;
+ current = current->next_;
+ }
+ ASSERT(i == waiters_);
+ result[i] = NULL;
+ waiters_head_ = waiters_tail_ = NULL;
+ waiters_ = 0;
+ LeaveCriticalSection(&waiters_cs_);
+ return result;
+}
+
+
+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,42 @@ 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_);
+ // Extract the first element in the list of waiters.
+ MonitorWaitData* head = data_.RemoveFirstWaiter();
+
+ // Signal the first element if there was one.
+ if (head != NULL) {
+ BOOL result = SetEvent(head->event_);
+ if (result == 0) {
+ FATAL("Monitor::Notify failed to signal event");
+ }
}
- LeaveCriticalSection(&data_.waiters_cs_);
}
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_);
+ // Extract the entire list of waiters into a separate array. Do not
+ // use the linked-list structure embedded in the MonitorWaitData
+ // objects. If a timeout occurs on an object in the array while we
+ // are processing them the MonitorWaitData object can be reused and
+ // the link structure can change. That should not influence which
+ // events are signaled here so we use a separate array.
+ //
cshapiro 2012/02/21 18:49:36 When a thread waits on a monitor and the wait time
Mads Ager (google) 2012/02/22 12:32:51 I don't understand. The notification loop over the
+ // If one of the objects in the array 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.
+ MonitorWaitData** waiters = data_.RemoveAllWaiters();
+
+ // Signal all the waiters.
+ for (intptr_t i = 0; waiters[i] != NULL; i++) {
cshapiro 2012/02/21 18:49:36 As noted in the header, this should be written as
Mads Ager (google) 2012/02/22 12:32:51 This could be expensive, I agree. However, it is m
+ BOOL result = SetEvent(waiters[i]->event_);
+ if (result == 0) {
+ FATAL("Monitor::NotifyAll failed to signal event");
+ }
}
- LeaveCriticalSection(&data_.waiters_cs_);
+
+ free(waiters);
}
} // namespace dart

Powered by Google App Engine
This is Rietveld 408576698