blob: a71d4b6b6f34199fd04e7203b60bae68d5846320 [file] [log] [blame]
// 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)