mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Matthew Dempsky](/assets/img/avatar_default.png)
Updates #37244. Change-Id: I32a5180a17fe43be5e18367d784cf756dffc6aeb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106009 Commit-Queue: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Régis Crelier <regis@google.com>
240 lines
5.6 KiB
C++
240 lines
5.6 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"
|
|
#include "vm/lockers.h"
|
|
#include "vm/os.h"
|
|
#include "vm/unit_test.h"
|
|
|
|
namespace dart {
|
|
|
|
DECLARE_FLAG(int, worker_timeout_millis);
|
|
|
|
VM_UNIT_TEST_CASE(ThreadPool_Create) {
|
|
ThreadPool thread_pool;
|
|
}
|
|
|
|
class TestTask : public ThreadPool::Task {
|
|
public:
|
|
TestTask(Monitor* sync, bool* done) : sync_(sync), done_(done) {}
|
|
|
|
// Before running the task, *done_ should be true. This lets the caller
|
|
// ASSERT things knowing that the thread is still around. To unblock the
|
|
// thread, the caller should take the lock, set *done_ to false, and Notify()
|
|
// the monitor.
|
|
virtual void Run() {
|
|
{
|
|
MonitorLocker ml(sync_);
|
|
while (*done_) {
|
|
ml.Wait();
|
|
}
|
|
}
|
|
MonitorLocker ml(sync_);
|
|
*done_ = true;
|
|
ml.Notify();
|
|
}
|
|
|
|
private:
|
|
Monitor* sync_;
|
|
bool* done_;
|
|
};
|
|
|
|
VM_UNIT_TEST_CASE(ThreadPool_RunOne) {
|
|
ThreadPool thread_pool;
|
|
Monitor sync;
|
|
bool done = true;
|
|
thread_pool.Run<TestTask>(&sync, &done);
|
|
{
|
|
MonitorLocker ml(&sync);
|
|
done = false;
|
|
ml.Notify();
|
|
while (!done) {
|
|
ml.Wait();
|
|
}
|
|
}
|
|
EXPECT(done);
|
|
|
|
// Do a sanity test on the worker stats.
|
|
EXPECT_EQ(1U, thread_pool.workers_started());
|
|
EXPECT_EQ(0U, thread_pool.workers_stopped());
|
|
}
|
|
|
|
VM_UNIT_TEST_CASE(ThreadPool_RunMany) {
|
|
const int kTaskCount = 100;
|
|
ThreadPool thread_pool;
|
|
Monitor sync[kTaskCount];
|
|
bool done[kTaskCount];
|
|
|
|
for (int i = 0; i < kTaskCount; i++) {
|
|
done[i] = true;
|
|
thread_pool.Run<TestTask>(&sync[i], &done[i]);
|
|
}
|
|
for (int i = 0; i < kTaskCount; i++) {
|
|
MonitorLocker ml(&sync[i]);
|
|
done[i] = false;
|
|
ml.Notify();
|
|
while (!done[i]) {
|
|
ml.Wait();
|
|
}
|
|
EXPECT(done[i]);
|
|
}
|
|
}
|
|
|
|
class SleepTask : public ThreadPool::Task {
|
|
public:
|
|
SleepTask(Monitor* sync, int* started_count, int* slept_count, int millis)
|
|
: sync_(sync),
|
|
started_count_(started_count),
|
|
slept_count_(slept_count),
|
|
millis_(millis) {}
|
|
|
|
virtual void Run() {
|
|
{
|
|
MonitorLocker ml(sync_);
|
|
*started_count_ = *started_count_ + 1;
|
|
ml.Notify();
|
|
}
|
|
// Sleep so we can be sure the ThreadPool destructor blocks until we're
|
|
// done.
|
|
OS::Sleep(millis_);
|
|
{
|
|
MonitorLocker ml(sync_);
|
|
*slept_count_ = *slept_count_ + 1;
|
|
// No notification here. The main thread is blocked in ThreadPool
|
|
// shutdown waiting for this thread to finish.
|
|
}
|
|
}
|
|
|
|
private:
|
|
Monitor* sync_;
|
|
int* started_count_;
|
|
int* slept_count_;
|
|
int millis_;
|
|
};
|
|
|
|
VM_UNIT_TEST_CASE(ThreadPool_WorkerShutdown) {
|
|
const int kTaskCount = 10;
|
|
Monitor sync;
|
|
int slept_count = 0;
|
|
int started_count = 0;
|
|
|
|
// Set up the ThreadPool so that workers notify before they exit.
|
|
ThreadPool* thread_pool = new ThreadPool();
|
|
|
|
// Run a single task.
|
|
for (int i = 0; i < kTaskCount; i++) {
|
|
thread_pool->Run<SleepTask>(&sync, &started_count, &slept_count, 2);
|
|
}
|
|
|
|
{
|
|
// Wait for everybody to start.
|
|
MonitorLocker ml(&sync);
|
|
while (started_count < kTaskCount) {
|
|
ml.Wait();
|
|
}
|
|
}
|
|
|
|
// Kill the thread pool while the workers are sleeping.
|
|
delete thread_pool;
|
|
thread_pool = NULL;
|
|
|
|
int final_count = 0;
|
|
{
|
|
MonitorLocker ml(&sync);
|
|
final_count = slept_count;
|
|
}
|
|
|
|
// We should have waited for all the workers to finish, so they all should
|
|
// have had a chance to increment slept_count.
|
|
EXPECT_EQ(kTaskCount, final_count);
|
|
}
|
|
|
|
VM_UNIT_TEST_CASE(ThreadPool_WorkerTimeout) {
|
|
// Adjust the worker timeout so that we timeout quickly.
|
|
int saved_timeout = FLAG_worker_timeout_millis;
|
|
FLAG_worker_timeout_millis = 1;
|
|
|
|
ThreadPool thread_pool;
|
|
EXPECT_EQ(0U, thread_pool.workers_started());
|
|
EXPECT_EQ(0U, thread_pool.workers_stopped());
|
|
|
|
// Run a worker.
|
|
Monitor sync;
|
|
bool done = true;
|
|
thread_pool.Run<TestTask>(&sync, &done);
|
|
EXPECT_EQ(1U, thread_pool.workers_started());
|
|
EXPECT_EQ(0U, thread_pool.workers_stopped());
|
|
{
|
|
MonitorLocker ml(&sync);
|
|
done = false;
|
|
ml.Notify();
|
|
while (!done) {
|
|
ml.Wait();
|
|
}
|
|
}
|
|
EXPECT(done);
|
|
|
|
// Wait up to 5 seconds to see if a worker times out.
|
|
const int kMaxWait = 5000;
|
|
int waited = 0;
|
|
while (thread_pool.workers_stopped() == 0 && waited < kMaxWait) {
|
|
OS::Sleep(1);
|
|
waited += 1;
|
|
}
|
|
EXPECT_EQ(1U, thread_pool.workers_stopped());
|
|
FLAG_worker_timeout_millis = saved_timeout;
|
|
}
|
|
|
|
class SpawnTask : public ThreadPool::Task {
|
|
public:
|
|
SpawnTask(ThreadPool* pool, Monitor* sync, int todo, int total, int* done)
|
|
: pool_(pool), sync_(sync), todo_(todo), total_(total), done_(done) {}
|
|
|
|
virtual void Run() {
|
|
todo_--; // Subtract one for current task.
|
|
int child_todo = todo_ / 2;
|
|
|
|
// Spawn 0-2 children.
|
|
if (todo_ > 0) {
|
|
pool_->Run<SpawnTask>(pool_, sync_, todo_ - child_todo, total_, done_);
|
|
}
|
|
if (todo_ > 1) {
|
|
pool_->Run<SpawnTask>(pool_, sync_, child_todo, total_, done_);
|
|
}
|
|
|
|
{
|
|
MonitorLocker ml(sync_);
|
|
(*done_)++;
|
|
if (*done_ >= total_) {
|
|
ml.Notify();
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
ThreadPool* pool_;
|
|
Monitor* sync_;
|
|
int todo_;
|
|
int total_;
|
|
int* done_;
|
|
};
|
|
|
|
VM_UNIT_TEST_CASE(ThreadPool_RecursiveSpawn) {
|
|
ThreadPool thread_pool;
|
|
Monitor sync;
|
|
const int kTotalTasks = 500;
|
|
int done = 0;
|
|
thread_pool.Run<SpawnTask>(&thread_pool, &sync, kTotalTasks, kTotalTasks,
|
|
&done);
|
|
{
|
|
MonitorLocker ml(&sync);
|
|
while (done < kTotalTasks) {
|
|
ml.Wait();
|
|
}
|
|
}
|
|
EXPECT_EQ(kTotalTasks, done);
|
|
}
|
|
|
|
} // namespace dart
|