| // 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/globals.h" // NOLINT |
| #if defined(TARGET_OS_WINDOWS) |
| |
| #include "vm/os_thread.h" |
| |
| #include <process.h> // NOLINT |
| |
| #include "platform/assert.h" |
| |
| namespace dart { |
| |
| class ThreadStartData { |
| public: |
| ThreadStartData(OSThread::ThreadStartFunction function, uword parameter) |
| : function_(function), parameter_(parameter) {} |
| |
| OSThread::ThreadStartFunction function() const { return function_; } |
| uword parameter() const { return parameter_; } |
| |
| private: |
| OSThread::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<ThreadStartData*>(data_ptr); |
| |
| OSThread::ThreadStartFunction function = data->function(); |
| uword parameter = data->parameter(); |
| delete data; |
| |
| MonitorData::GetMonitorWaitDataForThread(); |
| |
| // Call the supplied thread start function handing it its parameters. |
| function(parameter); |
| |
| // Clean up the monitor wait data for this thread. |
| MonitorWaitData::ThreadExit(); |
| |
| return 0; |
| } |
| |
| |
| int OSThread::Start(ThreadStartFunction function, uword parameter) { |
| ThreadStartData* start_data = new ThreadStartData(function, parameter); |
| uint32_t tid; |
| uintptr_t thread = _beginthreadex(NULL, OSThread::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; |
| } |
| |
| // Close the handle, so we don't leak the thread object. |
| CloseHandle(reinterpret_cast<HANDLE>(thread)); |
| |
| return 0; |
| } |
| |
| ThreadLocalKey OSThread::kUnsetThreadLocalKey = TLS_OUT_OF_INDEXES; |
| ThreadId OSThread::kInvalidThreadId = 0; |
| ThreadJoinId OSThread::kInvalidThreadJoinId = 0; |
| |
| ThreadLocalKey OSThread::CreateThreadLocal(ThreadDestructor unused) { |
| ThreadLocalKey key = TlsAlloc(); |
| if (key == kUnsetThreadLocalKey) { |
| FATAL1("TlsAlloc failed %d", GetLastError()); |
| } |
| return key; |
| } |
| |
| |
| void OSThread::DeleteThreadLocal(ThreadLocalKey key) { |
| ASSERT(key != kUnsetThreadLocalKey); |
| BOOL result = TlsFree(key); |
| if (!result) { |
| FATAL1("TlsFree failed %d", GetLastError()); |
| } |
| } |
| |
| |
| intptr_t OSThread::GetMaxStackSize() { |
| const int kStackSize = (128 * kWordSize * KB); |
| return kStackSize; |
| } |
| |
| |
| ThreadId OSThread::GetCurrentThreadId() { |
| return ::GetCurrentThreadId(); |
| } |
| |
| |
| ThreadId OSThread::GetCurrentThreadTraceId() { |
| return ::GetCurrentThreadId(); |
| } |
| |
| |
| ThreadJoinId OSThread::GetCurrentThreadJoinId() { |
| return ::GetCurrentThreadId(); |
| } |
| |
| |
| void OSThread::Join(ThreadJoinId id) { |
| HANDLE handle = OpenThread(SYNCHRONIZE, false, id); |
| |
| // TODO(zra): OSThread::Start() closes the handle to the thread. Thus, by the |
| // time we try to join the thread, its resources may have already been |
| // reclaimed, and joining will fail. This can be avoided in a couple of ways. |
| // First, GetCurrentThreadJoinId could call OpenThread and return a handle. |
| // This is bad, because each of those handles would have to be closed. |
| // Second OSThread could be refactored to no longer be AllStatic. Then the |
| // handle could be cached in the object by the Start method. |
| if (handle == NULL) { |
| return; |
| } |
| |
| DWORD res = WaitForSingleObject(handle, INFINITE); |
| CloseHandle(handle); |
| ASSERT(res == WAIT_OBJECT_0); |
| } |
| |
| |
| intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { |
| ASSERT(sizeof(id) <= sizeof(intptr_t)); |
| return static_cast<intptr_t>(id); |
| } |
| |
| |
| ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) { |
| return static_cast<ThreadId>(id); |
| } |
| |
| |
| bool OSThread::Compare(ThreadId a, ThreadId b) { |
| return a == b; |
| } |
| |
| |
| void OSThread::GetThreadCpuUsage(ThreadId thread_id, int64_t* cpu_usage) { |
| static const int64_t kTimeEpoc = 116444736000000000LL; |
| static const int64_t kTimeScaler = 10; // 100 ns to us. |
| // Although win32 uses 64-bit integers for representing timestamps, |
| // these are packed into a FILETIME structure. The FILETIME |
| // structure is just a struct representing a 64-bit integer. The |
| // TimeStamp union allows access to both a FILETIME and an integer |
| // representation of the timestamp. The Windows timestamp is in |
| // 100-nanosecond intervals since January 1, 1601. |
| union TimeStamp { |
| FILETIME ft_; |
| int64_t t_; |
| }; |
| ASSERT(cpu_usage != NULL); |
| TimeStamp created; |
| TimeStamp exited; |
| TimeStamp kernel; |
| TimeStamp user; |
| HANDLE handle = OpenThread(THREAD_QUERY_INFORMATION, false, thread_id); |
| BOOL result = GetThreadTimes(handle, |
| &created.ft_, |
| &exited.ft_, |
| &kernel.ft_, |
| &user.ft_); |
| CloseHandle(handle); |
| if (!result) { |
| FATAL1("GetThreadCpuUsage failed %d\n", GetLastError()); |
| } |
| *cpu_usage = (user.t_ - kTimeEpoc) / kTimeScaler; |
| } |
| |
| |
| void OSThread::SetThreadLocal(ThreadLocalKey key, uword value) { |
| ASSERT(key != kUnsetThreadLocalKey); |
| BOOL result = TlsSetValue(key, reinterpret_cast<void*>(value)); |
| if (!result) { |
| FATAL1("TlsSetValue failed %d", GetLastError()); |
| } |
| } |
| |
| |
| Mutex::Mutex() { |
| // Allocate unnamed semaphore with initial count 1 and max count 1. |
| data_.semaphore_ = CreateSemaphore(NULL, 1, 1, NULL); |
| if (data_.semaphore_ == NULL) { |
| FATAL1("Mutex allocation failed %d", GetLastError()); |
| } |
| // When running with assertions enabled we do track the owner. |
| #if defined(DEBUG) |
| owner_ = OSThread::kInvalidThreadId; |
| #endif // defined(DEBUG) |
| } |
| |
| |
| Mutex::~Mutex() { |
| CloseHandle(data_.semaphore_); |
| // When running with assertions enabled we do track the owner. |
| #if defined(DEBUG) |
| ASSERT(owner_ == OSThread::kInvalidThreadId); |
| #endif // defined(DEBUG) |
| } |
| |
| |
| void Mutex::Lock() { |
| DWORD result = WaitForSingleObject(data_.semaphore_, INFINITE); |
| if (result != WAIT_OBJECT_0) { |
| FATAL1("Mutex lock failed %d", GetLastError()); |
| } |
| // When running with assertions enabled we do track the owner. |
| #if defined(DEBUG) |
| owner_ = OSThread::GetCurrentThreadId(); |
| #endif // defined(DEBUG) |
| } |
| |
| |
| bool Mutex::TryLock() { |
| // Attempt to pass the semaphore but return immediately. |
| DWORD result = WaitForSingleObject(data_.semaphore_, 0); |
| if (result == WAIT_OBJECT_0) { |
| // When running with assertions enabled we do track the owner. |
| #if defined(DEBUG) |
| owner_ = OSThread::GetCurrentThreadId(); |
| #endif // defined(DEBUG) |
| return true; |
| } |
| if (result == WAIT_ABANDONED || result == WAIT_FAILED) { |
| FATAL1("Mutex try lock failed %d", GetLastError()); |
| } |
| ASSERT(result == WAIT_TIMEOUT); |
| return false; |
| } |
| |
| |
| void Mutex::Unlock() { |
| // When running with assertions enabled we do track the owner. |
| #if defined(DEBUG) |
| ASSERT(IsOwnedByCurrentThread()); |
| owner_ = OSThread::kInvalidThreadId; |
| #endif // defined(DEBUG) |
| BOOL result = ReleaseSemaphore(data_.semaphore_, 1, NULL); |
| if (result == 0) { |
| FATAL1("Mutex unlock failed %d", GetLastError()); |
| } |
| } |
| |
| |
| ThreadLocalKey MonitorWaitData::monitor_wait_data_key_ = |
| OSThread::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 MonitorWaitData::ThreadExit() { |
| if (MonitorWaitData::monitor_wait_data_key_ != |
| OSThread::kUnsetThreadLocalKey) { |
| uword raw_wait_data = |
| OSThread::GetThreadLocal(MonitorWaitData::monitor_wait_data_key_); |
| if (raw_wait_data != 0) { |
| MonitorWaitData* wait_data = |
| reinterpret_cast<MonitorWaitData*>(raw_wait_data); |
| delete wait_data; |
| } |
| } |
| } |
| |
| |
| 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_; |
| } |
| // Clear next. |
| wait_data->next_ = NULL; |
| 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_; |
| } |
| // Clear next. |
| first->next_ = NULL; |
| // Signal event. |
| BOOL result = SetEvent(first->event_); |
| if (result == 0) { |
| FATAL1("Monitor::Notify failed to signal event %d", GetLastError()); |
| } |
| } |
| 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) { |
| // Copy next. |
| MonitorWaitData* next = current->next_; |
| // Clear next. |
| current->next_ = NULL; |
| // Signal event. |
| BOOL result = SetEvent(current->event_); |
| if (result == 0) { |
| FATAL1("Failed to set event for NotifyAll %d", GetLastError()); |
| } |
| current = next; |
| } |
| LeaveCriticalSection(&waiters_cs_); |
| } |
| |
| |
| MonitorWaitData* MonitorData::GetMonitorWaitDataForThread() { |
| // Ensure that the thread local key for monitor wait data objects is |
| // initialized. |
| ASSERT(MonitorWaitData::monitor_wait_data_key_ != |
| OSThread::kUnsetThreadLocalKey); |
| |
| // 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 = |
| OSThread::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); |
| OSThread::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; |
| |
| // Get the wait data object containing the event to wait for. |
| MonitorWaitData* wait_data = MonitorData::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) { |
| FATAL1("Monitor::Wait failed %d", GetLastError()); |
| } |
| } 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) { |
| FATAL1("Monitor::Wait with timeout failed %d", GetLastError()); |
| } |
| 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; |
| } |
| |
| |
| Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { |
| // TODO(johnmccutchan): Investigate sub-millisecond sleep times on Windows. |
| int64_t millis = micros / kMicrosecondsPerMillisecond; |
| if ((millis * kMicrosecondsPerMillisecond) < micros) { |
| // We've been asked to sleep for a fraction of a millisecond, |
| // this isn't supported on Windows. Bumps milliseconds up by one |
| // so that we never return too early. We likely return late though. |
| millis += 1; |
| } |
| return Wait(millis); |
| } |
| |
| |
| 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 |
| |
| #endif // defined(TARGET_OS_WINDOWS) |