9d72035ce5
each isolate or native port had a dedicated thread. Refactored the MessageHandler api... - Added a Run function to allow a MessageHandler to run on a ThreadPool. These functions take a start and end callback to allow for isolate initialization and shutdown. - Made the queue private to the MessageHandler and moved all message processing code inside the MessageHandler (got rid of all of the different flavors of RunLoop). This helps remove some code duplication and hides the details of how messages are handled. - Moved all locking and notification out of MessageQueue and moved it up to MessageHandler. Moved OOB support out of MessageQueue and up to MessageHandler. These changes make the MessageQueue much simpler. - Refactored native port and isolate MessageHandlers to share more code. - Improved --trace_isolates output. - Added tests for MessageHandler. Refactored lib/isolate code... - Use the new MessageHandler::Run api. - Got rid of the LongJump stuff in RunIsolate. No longer needed. - Use the new StartIsolateScope/SwitchIsolateScope to make the code less verbose and less error-prone. - Store top-level isolate errors in the sticky_error. Added StartIsolateScope/SwitchIsolateScope classes. Review URL: https://chromiumcodereview.appspot.com//9924015 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@6762 260f80e4-7a28-3924-810f-c04153c831b5
332 lines
7.4 KiB
C++
332 lines
7.4 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 "vm/thread_pool.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(int, worker_timeout_millis, 5000,
|
|
"Free workers when they have been idle for this amount of time.");
|
|
|
|
Monitor* ThreadPool::exit_monitor_ = NULL;
|
|
int* ThreadPool::exit_count_ = NULL;
|
|
|
|
ThreadPool::ThreadPool()
|
|
: shutting_down_(false),
|
|
all_workers_(NULL),
|
|
idle_workers_(NULL),
|
|
count_started_(0),
|
|
count_stopped_(0),
|
|
count_running_(0),
|
|
count_idle_(0) {
|
|
}
|
|
|
|
|
|
ThreadPool::~ThreadPool() {
|
|
Shutdown();
|
|
}
|
|
|
|
|
|
void ThreadPool::Run(Task* task) {
|
|
Worker* worker = NULL;
|
|
bool new_worker = false;
|
|
{
|
|
// We need ThreadPool::mutex_ to access worker lists and other
|
|
// ThreadPool state.
|
|
MutexLocker ml(&mutex_);
|
|
if (shutting_down_) {
|
|
return;
|
|
}
|
|
if (idle_workers_ == NULL) {
|
|
worker = new Worker(this);
|
|
ASSERT(worker != NULL);
|
|
new_worker = true;
|
|
count_started_++;
|
|
|
|
// Add worker to the all_workers_ list.
|
|
worker->all_next_ = all_workers_;
|
|
all_workers_ = worker;
|
|
worker->owned_ = true;
|
|
} else {
|
|
// Get the first worker from the idle worker list.
|
|
worker = idle_workers_;
|
|
idle_workers_ = worker->idle_next_;
|
|
worker->idle_next_ = NULL;
|
|
count_idle_--;
|
|
}
|
|
count_running_++;
|
|
}
|
|
// Release ThreadPool::mutex_ before calling Worker functions.
|
|
ASSERT(worker != NULL);
|
|
worker->SetTask(task);
|
|
if (new_worker) {
|
|
// Call StartThread after we've assigned the first task.
|
|
worker->StartThread();
|
|
}
|
|
}
|
|
|
|
|
|
void ThreadPool::Shutdown() {
|
|
Worker* saved = NULL;
|
|
{
|
|
MutexLocker ml(&mutex_);
|
|
shutting_down_ = true;
|
|
saved = all_workers_;
|
|
all_workers_ = NULL;
|
|
idle_workers_ = NULL;
|
|
|
|
Worker* current = saved;
|
|
while (current != NULL) {
|
|
Worker* next = current->all_next_;
|
|
current->idle_next_ = NULL;
|
|
current->owned_ = false;
|
|
current = next;
|
|
count_stopped_++;
|
|
}
|
|
|
|
count_idle_ = 0;
|
|
count_running_ = 0;
|
|
ASSERT(count_started_ == count_stopped_);
|
|
}
|
|
// Release ThreadPool::mutex_ before calling Worker functions.
|
|
|
|
Worker* current = saved;
|
|
while (current != NULL) {
|
|
// We may access all_next_ without holding ThreadPool::mutex_ here
|
|
// because the worker is no longer owned by the ThreadPool.
|
|
Worker* next = current->all_next_;
|
|
current->all_next_ = NULL;
|
|
current->Shutdown();
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
|
|
bool ThreadPool::IsIdle(Worker* worker) {
|
|
ASSERT(worker != NULL && worker->owned_);
|
|
for (Worker* current = idle_workers_;
|
|
current != NULL;
|
|
current = current->idle_next_) {
|
|
if (current == worker) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ThreadPool::RemoveWorkerFromIdleList(Worker* worker) {
|
|
ASSERT(worker != NULL && worker->owned_);
|
|
if (idle_workers_ == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// Special case head of list.
|
|
if (idle_workers_ == worker) {
|
|
idle_workers_ = worker->idle_next_;
|
|
worker->idle_next_ = NULL;
|
|
return true;
|
|
}
|
|
|
|
for (Worker* current = idle_workers_;
|
|
current->idle_next_ != NULL;
|
|
current = current->idle_next_) {
|
|
if (current->idle_next_ == worker) {
|
|
current->idle_next_ = worker->idle_next_;
|
|
worker->idle_next_ = NULL;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ThreadPool::RemoveWorkerFromAllList(Worker* worker) {
|
|
ASSERT(worker != NULL && worker->owned_);
|
|
if (all_workers_ == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// Special case head of list.
|
|
if (all_workers_ == worker) {
|
|
all_workers_ = worker->all_next_;
|
|
worker->all_next_ = NULL;
|
|
worker->owned_ = false;
|
|
worker->pool_ = NULL;
|
|
return true;
|
|
}
|
|
|
|
for (Worker* current = all_workers_;
|
|
current->all_next_ != NULL;
|
|
current = current->all_next_) {
|
|
if (current->all_next_ == worker) {
|
|
current->all_next_ = worker->all_next_;
|
|
worker->all_next_ = NULL;
|
|
worker->owned_ = false;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void ThreadPool::SetIdle(Worker* worker) {
|
|
MutexLocker ml(&mutex_);
|
|
if (shutting_down_) {
|
|
return;
|
|
}
|
|
ASSERT(worker->owned_ && !IsIdle(worker));
|
|
worker->idle_next_ = idle_workers_;
|
|
idle_workers_ = worker;
|
|
count_idle_++;
|
|
count_running_--;
|
|
}
|
|
|
|
|
|
bool ThreadPool::ReleaseIdleWorker(Worker* worker) {
|
|
MutexLocker ml(&mutex_);
|
|
if (shutting_down_) {
|
|
return false;
|
|
}
|
|
// Remove from idle list.
|
|
if (!RemoveWorkerFromIdleList(worker)) {
|
|
return false;
|
|
}
|
|
// Remove from all list.
|
|
bool found = RemoveWorkerFromAllList(worker);
|
|
ASSERT(found);
|
|
|
|
count_stopped_++;
|
|
count_idle_--;
|
|
return true;
|
|
}
|
|
|
|
|
|
ThreadPool::Task::Task() {
|
|
}
|
|
|
|
|
|
ThreadPool::Task::~Task() {
|
|
}
|
|
|
|
|
|
ThreadPool::Worker::Worker(ThreadPool* pool)
|
|
: pool_(pool),
|
|
task_(NULL),
|
|
owned_(false),
|
|
all_next_(NULL),
|
|
idle_next_(NULL) {
|
|
}
|
|
|
|
|
|
void ThreadPool::Worker::StartThread() {
|
|
#if defined(DEBUG)
|
|
// Must call SetTask before StartThread.
|
|
{ // NOLINT
|
|
MonitorLocker ml(&monitor_);
|
|
ASSERT(task_ != NULL);
|
|
}
|
|
#endif
|
|
int result = Thread::Start(&Worker::Main, reinterpret_cast<uword>(this));
|
|
if (result != 0) {
|
|
FATAL1("Could not start worker thread: result = %d.", result);
|
|
}
|
|
}
|
|
|
|
|
|
void ThreadPool::Worker::SetTask(Task* task) {
|
|
MonitorLocker ml(&monitor_);
|
|
ASSERT(task_ == NULL);
|
|
task_ = task;
|
|
ml.Notify();
|
|
}
|
|
|
|
|
|
static int64_t ComputeTimeout(int64_t idle_start) {
|
|
if (FLAG_worker_timeout_millis <= 0) {
|
|
// No timeout.
|
|
return 0;
|
|
} else {
|
|
int64_t waited = OS::GetCurrentTimeMillis() - idle_start;
|
|
if (waited >= FLAG_worker_timeout_millis) {
|
|
// We must have gotten a spurious wakeup just before we timed
|
|
// out. Give the worker one last desperate chance to live. We
|
|
// are merciful.
|
|
return 1;
|
|
} else {
|
|
return FLAG_worker_timeout_millis - waited;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ThreadPool::Worker::Loop() {
|
|
MonitorLocker ml(&monitor_);
|
|
int64_t idle_start;
|
|
while (true) {
|
|
ASSERT(task_ != NULL);
|
|
Task* task = task_;
|
|
task_ = NULL;
|
|
|
|
// Release monitor while handling the task.
|
|
monitor_.Exit();
|
|
task->Run();
|
|
delete task;
|
|
monitor_.Enter();
|
|
|
|
ASSERT(task_ == NULL);
|
|
if (IsDone()) {
|
|
return;
|
|
}
|
|
ASSERT(pool_ != NULL);
|
|
pool_->SetIdle(this);
|
|
idle_start = OS::GetCurrentTimeMillis();
|
|
while (true) {
|
|
Monitor::WaitResult result = ml.Wait(ComputeTimeout(idle_start));
|
|
if (task_ != NULL) {
|
|
// We've found a task. Process it, regardless of whether the
|
|
// worker is done_.
|
|
break;
|
|
}
|
|
if (IsDone()) {
|
|
return;
|
|
}
|
|
if (result == Monitor::kTimedOut &&
|
|
pool_->ReleaseIdleWorker(this)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
|
|
void ThreadPool::Worker::Shutdown() {
|
|
MonitorLocker ml(&monitor_);
|
|
pool_ = NULL; // Fail fast if someone tries to access pool_.
|
|
ml.Notify();
|
|
}
|
|
|
|
|
|
// static
|
|
void ThreadPool::Worker::Main(uword args) {
|
|
Worker* worker = reinterpret_cast<Worker*>(args);
|
|
worker->Loop();
|
|
|
|
// It should be okay to access these unlocked here in this assert.
|
|
ASSERT(!worker->owned_ &&
|
|
worker->all_next_ == NULL &&
|
|
worker->idle_next_ == NULL);
|
|
|
|
// The exit monitor is only used during testing.
|
|
if (ThreadPool::exit_monitor_) {
|
|
MonitorLocker ml(ThreadPool::exit_monitor_);
|
|
(*ThreadPool::exit_count_)++;
|
|
ml.Notify();
|
|
}
|
|
delete worker;
|
|
}
|
|
|
|
} // namespace dart
|