// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "platform/thread.h" #include #include "platform/assert.h" namespace dart { class ThreadStartData { public: ThreadStartData(Thread::ThreadStartFunction function, uword parameter) : function_(function), parameter_(parameter) {} Thread::ThreadStartFunction function() const { return function_; } uword parameter() const { return parameter_; } private: Thread::ThreadStartFunction function_; uword parameter_; DISALLOW_COPY_AND_ASSIGN(ThreadStartData); }; // Dispatch to the thread start function provided by the caller. This trampoline // is used to ensure that the thread is properly destroyed if the thread just // exits. static unsigned int __stdcall ThreadEntry(void* data_ptr) { ThreadStartData* data = reinterpret_cast(data_ptr); Thread::ThreadStartFunction function = data->function(); uword parameter = data->parameter(); delete data; // Call the supplied thread start function handing it its parameters. function(parameter); // When the function returns here close the handle. CloseHandle(GetCurrentThread()); return 0; } int Thread::Start(ThreadStartFunction function, uword parameter) { ThreadStartData* start_data = new ThreadStartData(function, parameter); uint32_t tid; uintptr_t thread = _beginthreadex(NULL, Thread::GetMaxStackSize(), ThreadEntry, start_data, 0, &tid); if (thread == -1L || thread == 0) { #ifdef DEBUG fprintf(stderr, "_beginthreadex error: %d (%s)\n", errno, strerror(errno)); #endif return errno; } return 0; } ThreadLocalKey Thread::kUnsetThreadLocalKey = TLS_OUT_OF_INDEXES; ThreadLocalKey Thread::CreateThreadLocal() { ThreadLocalKey key = TlsAlloc(); if (key == kUnsetThreadLocalKey) { FATAL("TlsAlloc failed"); } return key; } void Thread::DeleteThreadLocal(ThreadLocalKey key) { ASSERT(key != kUnsetThreadLocalKey); BOOL result = TlsFree(key); if (!result) { FATAL("TlsFree failed"); } } intptr_t Thread::GetMaxStackSize() { const int kStackSize = (128 * kWordSize * KB); return kStackSize; } void Thread::SetThreadLocal(ThreadLocalKey key, uword value) { ASSERT(key != kUnsetThreadLocalKey); BOOL result = TlsSetValue(key, reinterpret_cast(value)); if (!result) { FATAL("TlsSetValue failed"); } } Mutex::Mutex() { // Allocate unnamed semaphore with initial count 1 and max count 1. data_.semaphore_ = CreateSemaphore(NULL, 1, 1, NULL); if (data_.semaphore_ == NULL) { FATAL("Mutex allocation failed"); } } Mutex::~Mutex() { CloseHandle(data_.semaphore_); } void Mutex::Lock() { DWORD result = WaitForSingleObject(data_.semaphore_, INFINITE); if (result != WAIT_OBJECT_0) { FATAL("Mutex lock failed"); } } bool Mutex::TryLock() { // Attempt to pass the semaphore but return immediately. DWORD result = WaitForSingleObject(data_.semaphore_, 0); if (result == WAIT_OBJECT_0) { return true; } if (result == WAIT_ABANDONED || result == WAIT_FAILED) { FATAL("Mutex try lock failed"); } ASSERT(result == WAIT_TIMEOUT); return false; } void Mutex::Unlock() { BOOL result = ReleaseSemaphore(data_.semaphore_, 1, NULL); if (result == 0) { FATAL("Mutex unlock failed"); } } ThreadLocalKey MonitorWaitData::monitor_wait_data_key_ = Thread::kUnsetThreadLocalKey; Monitor::Monitor() { InitializeCriticalSection(&data_.cs_); InitializeCriticalSection(&data_.waiters_cs_); data_.waiters_head_ = NULL; data_.waiters_tail_ = NULL; } Monitor::~Monitor() { DeleteCriticalSection(&data_.cs_); DeleteCriticalSection(&data_.waiters_cs_); } void Monitor::Enter() { EnterCriticalSection(&data_.cs_); } void Monitor::Exit() { LeaveCriticalSection(&data_.cs_); } 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(wait_data)); } else { wait_data = reinterpret_cast(raw_wait_data); wait_data->next_ = NULL; } return wait_data; } Monitor::WaitResult Monitor::Wait(int64_t millis) { Monitor::WaitResult retval = kNotified; // 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 on the event. DWORD result = WAIT_FAILED; if (millis == 0) { // Wait forever for a Notify or a NotifyAll event. 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 = 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; } } // Reacquire the monitor critical section before continuing. EnterCriticalSection(&data_.cs_); return retval; } void Monitor::Notify() { data_.SignalAndRemoveFirstWaiter(); } void Monitor::NotifyAll() { // 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