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 |