9e19d236ca
fields in a thread (i.e fields that are not Dart VM related) - Split the Thread structure to be a pure Dart per thread structure and add a pointer to os_thread which points to the OSThread structure - Change Schedule/UnSchedule to set the Dart Thread structure as the TLS of the thread when it is inside the Dart world and reset the TLS back to the OSThread strcuture when is exits the Dart World. - Moved the stack_base and few stack size related functions to OSThread from Isolate R=johnmccutchan@google.com, zra@google.com Review URL: https://codereview.chromium.org/1439483003 .
708 lines
21 KiB
C++
708 lines
21 KiB
C++
// 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/growable_array.h"
|
|
#include "vm/os_thread.h"
|
|
|
|
#include <process.h> // NOLINT
|
|
|
|
#include "platform/assert.h"
|
|
|
|
namespace dart {
|
|
|
|
// This flag is flipped by platform_win.cc when the process is exiting.
|
|
// TODO(zra): Remove once VM shuts down cleanly.
|
|
bool private_flag_windows_run_tls_destructors = true;
|
|
|
|
class ThreadStartData {
|
|
public:
|
|
ThreadStartData(const char* name,
|
|
OSThread::ThreadStartFunction function,
|
|
uword parameter)
|
|
: name_(name), function_(function), parameter_(parameter) {}
|
|
|
|
const char* name() const { return name_; }
|
|
OSThread::ThreadStartFunction function() const { return function_; }
|
|
uword parameter() const { return parameter_; }
|
|
|
|
private:
|
|
const char* name_;
|
|
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);
|
|
|
|
const char* name = data->name();
|
|
OSThread::ThreadStartFunction function = data->function();
|
|
uword parameter = data->parameter();
|
|
delete data;
|
|
|
|
MonitorData::GetMonitorWaitDataForThread();
|
|
|
|
// Create new OSThread object and set as TLS for new thread.
|
|
OSThread* thread = new OSThread();
|
|
OSThread::SetCurrent(thread);
|
|
thread->set_name(name);
|
|
|
|
// 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(const char* name,
|
|
ThreadStartFunction function,
|
|
uword parameter) {
|
|
ThreadStartData* start_data = new ThreadStartData(name, 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 destructor) {
|
|
ThreadLocalKey key = TlsAlloc();
|
|
if (key == kUnsetThreadLocalKey) {
|
|
FATAL1("TlsAlloc failed %d", GetLastError());
|
|
}
|
|
ThreadLocalData::AddThreadLocal(key, destructor);
|
|
return key;
|
|
}
|
|
|
|
|
|
void OSThread::DeleteThreadLocal(ThreadLocalKey key) {
|
|
ASSERT(key != kUnsetThreadLocalKey);
|
|
BOOL result = TlsFree(key);
|
|
if (!result) {
|
|
FATAL1("TlsFree failed %d", GetLastError());
|
|
}
|
|
ThreadLocalData::RemoveThreadLocal(key);
|
|
}
|
|
|
|
|
|
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() {
|
|
// TODO(zra): Use the thread handle as the join id in order to have a more
|
|
// reliable join on windows.
|
|
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());
|
|
}
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we do track the owner.
|
|
owner_ = OSThread::kInvalidThreadId;
|
|
#endif // defined(DEBUG)
|
|
}
|
|
|
|
|
|
Mutex::~Mutex() {
|
|
CloseHandle(data_.semaphore_);
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we do track the owner.
|
|
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());
|
|
}
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we do track the owner.
|
|
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) {
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we do track the owner.
|
|
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() {
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we do track the owner.
|
|
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;
|
|
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we track the owner.
|
|
owner_ = OSThread::kInvalidThreadId;
|
|
#endif // defined(DEBUG)
|
|
}
|
|
|
|
|
|
Monitor::~Monitor() {
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we track the owner.
|
|
ASSERT(owner_ == OSThread::kInvalidThreadId);
|
|
#endif // defined(DEBUG)
|
|
|
|
DeleteCriticalSection(&data_.cs_);
|
|
DeleteCriticalSection(&data_.waiters_cs_);
|
|
}
|
|
|
|
|
|
void Monitor::Enter() {
|
|
EnterCriticalSection(&data_.cs_);
|
|
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we track the owner.
|
|
ASSERT(owner_ == OSThread::kInvalidThreadId);
|
|
owner_ = OSThread::GetCurrentThreadId();
|
|
#endif // defined(DEBUG)
|
|
}
|
|
|
|
|
|
void Monitor::Exit() {
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we track the owner.
|
|
ASSERT(IsOwnedByCurrentThread());
|
|
owner_ = OSThread::kInvalidThreadId;
|
|
#endif // defined(DEBUG)
|
|
|
|
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_);
|
|
// Clear in case this is called a second time.
|
|
OSThread::SetThreadLocal(MonitorWaitData::monitor_wait_data_key_, 0);
|
|
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) {
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we track the owner.
|
|
ASSERT(IsOwnedByCurrentThread());
|
|
ThreadId saved_owner = owner_;
|
|
owner_ = OSThread::kInvalidThreadId;
|
|
#endif // defined(DEBUG)
|
|
|
|
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_);
|
|
|
|
#if defined(DEBUG)
|
|
// When running with assertions enabled we track the owner.
|
|
ASSERT(owner_ == OSThread::kInvalidThreadId);
|
|
owner_ = OSThread::GetCurrentThreadId();
|
|
ASSERT(owner_ == saved_owner);
|
|
#endif // defined(DEBUG)
|
|
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() {
|
|
// When running with assertions enabled we track the owner.
|
|
ASSERT(IsOwnedByCurrentThread());
|
|
data_.SignalAndRemoveFirstWaiter();
|
|
}
|
|
|
|
|
|
void Monitor::NotifyAll() {
|
|
// When running with assertions enabled we track the owner.
|
|
ASSERT(IsOwnedByCurrentThread());
|
|
// 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();
|
|
}
|
|
|
|
|
|
void ThreadLocalData::AddThreadLocal(ThreadLocalKey key,
|
|
ThreadDestructor destructor) {
|
|
ASSERT(thread_locals_ != NULL);
|
|
if (destructor == NULL) {
|
|
// We only care about thread locals with destructors.
|
|
return;
|
|
}
|
|
mutex_->Lock();
|
|
#if defined(DEBUG)
|
|
// Verify that we aren't added twice.
|
|
for (intptr_t i = 0; i < thread_locals_->length(); i++) {
|
|
const ThreadLocalEntry& entry = thread_locals_->At(i);
|
|
ASSERT(entry.key() != key);
|
|
}
|
|
#endif
|
|
// Add to list.
|
|
thread_locals_->Add(ThreadLocalEntry(key, destructor));
|
|
mutex_->Unlock();
|
|
}
|
|
|
|
|
|
void ThreadLocalData::RemoveThreadLocal(ThreadLocalKey key) {
|
|
ASSERT(thread_locals_ != NULL); mutex_->Lock();
|
|
intptr_t i = 0;
|
|
for (; i < thread_locals_->length(); i++) {
|
|
const ThreadLocalEntry& entry = thread_locals_->At(i);
|
|
if (entry.key() == key) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == thread_locals_->length()) {
|
|
// Not found.
|
|
mutex_->Unlock();
|
|
return;
|
|
}
|
|
thread_locals_->RemoveAt(i);
|
|
mutex_->Unlock();
|
|
}
|
|
|
|
|
|
// This function is executed on the thread that is exiting. It is invoked
|
|
// by |OnDartThreadExit| (see below for notes on TLS destructors on Windows).
|
|
void ThreadLocalData::RunDestructors() {
|
|
ASSERT(thread_locals_ != NULL);
|
|
ASSERT(mutex_ != NULL);
|
|
mutex_->Lock();
|
|
for (intptr_t i = 0; i < thread_locals_->length(); i++) {
|
|
const ThreadLocalEntry& entry = thread_locals_->At(i);
|
|
// We access the exiting thread's TLS variable here.
|
|
void* p = reinterpret_cast<void*>(OSThread::GetThreadLocal(entry.key()));
|
|
// We invoke the constructor here.
|
|
entry.destructor()(p);
|
|
}
|
|
mutex_->Unlock();
|
|
}
|
|
|
|
|
|
Mutex* ThreadLocalData::mutex_ = NULL;
|
|
MallocGrowableArray<ThreadLocalEntry>* ThreadLocalData::thread_locals_ = NULL;
|
|
|
|
|
|
void ThreadLocalData::InitOnce() {
|
|
mutex_ = new Mutex();
|
|
thread_locals_ = new MallocGrowableArray<ThreadLocalEntry>();
|
|
}
|
|
|
|
|
|
void ThreadLocalData::Shutdown() {
|
|
if (mutex_ != NULL) {
|
|
delete mutex_;
|
|
mutex_ = NULL;
|
|
}
|
|
if (thread_locals_ != NULL) {
|
|
delete thread_locals_;
|
|
thread_locals_ = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace dart
|
|
|
|
// The following was adapted from Chromium:
|
|
// src/base/threading/thread_local_storage_win.cc
|
|
|
|
// Thread Termination Callbacks.
|
|
// Windows doesn't support a per-thread destructor with its
|
|
// TLS primitives. So, we build it manually by inserting a
|
|
// function to be called on each thread's exit.
|
|
// This magic is from http://www.codeproject.com/threads/tls.asp
|
|
// and it works for VC++ 7.0 and later.
|
|
|
|
// Force a reference to _tls_used to make the linker create the TLS directory
|
|
// if it's not already there. (e.g. if __declspec(thread) is not used).
|
|
// Force a reference to p_thread_callback_dart to prevent whole program
|
|
// optimization from discarding the variable.
|
|
#ifdef _WIN64
|
|
|
|
#pragma comment(linker, "/INCLUDE:_tls_used")
|
|
#pragma comment(linker, "/INCLUDE:p_thread_callback_dart")
|
|
|
|
#else // _WIN64
|
|
|
|
#pragma comment(linker, "/INCLUDE:__tls_used")
|
|
#pragma comment(linker, "/INCLUDE:_p_thread_callback_dart")
|
|
|
|
#endif // _WIN64
|
|
|
|
// Static callback function to call with each thread termination.
|
|
void NTAPI OnDartThreadExit(PVOID module, DWORD reason, PVOID reserved) {
|
|
if (!dart::private_flag_windows_run_tls_destructors) {
|
|
return;
|
|
}
|
|
// On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+
|
|
// and on W2K and W2K3. So don't assume it is sent.
|
|
if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) {
|
|
dart::ThreadLocalData::RunDestructors();
|
|
dart::MonitorWaitData::ThreadExit();
|
|
}
|
|
}
|
|
|
|
// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
|
|
// called automatically by the OS loader code (not the CRT) when the module is
|
|
// loaded and on thread creation. They are NOT called if the module has been
|
|
// loaded by a LoadLibrary() call. It must have implicitly been loaded at
|
|
// process startup.
|
|
// By implicitly loaded, I mean that it is directly referenced by the main EXE
|
|
// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being
|
|
// implicitly loaded.
|
|
//
|
|
// See VC\crt\src\tlssup.c for reference.
|
|
|
|
// extern "C" suppresses C++ name mangling so we know the symbol name for the
|
|
// linker /INCLUDE:symbol pragma above.
|
|
extern "C" {
|
|
// The linker must not discard p_thread_callback_dart. (We force a reference
|
|
// to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If
|
|
// this variable is discarded, the OnDartThreadExit function will never be
|
|
// called.
|
|
#ifdef _WIN64
|
|
|
|
// .CRT section is merged with .rdata on x64 so it must be constant data.
|
|
#pragma const_seg(".CRT$XLB")
|
|
// When defining a const variable, it must have external linkage to be sure the
|
|
// linker doesn't discard it.
|
|
extern const PIMAGE_TLS_CALLBACK p_thread_callback_dart;
|
|
const PIMAGE_TLS_CALLBACK p_thread_callback_dart = OnDartThreadExit;
|
|
|
|
// Reset the default section.
|
|
#pragma const_seg()
|
|
|
|
#else // _WIN64
|
|
|
|
#pragma data_seg(".CRT$XLB")
|
|
PIMAGE_TLS_CALLBACK p_thread_callback_dart = OnDartThreadExit;
|
|
|
|
// Reset the default section.
|
|
#pragma data_seg()
|
|
|
|
#endif // _WIN64
|
|
} // extern "C"
|
|
|
|
#endif // defined(TARGET_OS_WINDOWS)
|