Chromium Code Reviews| 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 |