Revert of Use background tasks for the compiler dispatcher (patchset #5 id:80001 of https://codereview.chromium.org/2606263002/ )

Reason for revert:
Causes IgnitionCompilerDispatcherTest.FinishNowWithBackgroundTask to fail.

https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20noi18n%20-%20debug/builds/11209

Original issue's description:
> Use background tasks for the compiler dispatcher
>
> BUG=v8:5215
> R=marja@chromium.org,vogelheim@chromium.org
>
> Review-Url: https://codereview.chromium.org/2606263002
> Cr-Commit-Position: refs/heads/master@{#42035}
> Committed: 7a1b3a7beb

TBR=marja@chromium.org,vogelheim@chromium.org,jochen@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:5215

Review-Url: https://codereview.chromium.org/2614433002
Cr-Commit-Position: refs/heads/master@{#42037}
This commit is contained in:
rmcilroy 2017-01-03 07:51:44 -08:00 committed by Commit bot
parent 37de62c991
commit d4b5bfe306
5 changed files with 43 additions and 433 deletions

View File

@ -93,18 +93,13 @@ void CancelableTaskManager::CancelAndWait() {
} }
} }
CancelableTask::CancelableTask(Isolate* isolate)
: CancelableTask(isolate, isolate->cancelable_task_manager()) {}
CancelableTask::CancelableTask(Isolate* isolate, CancelableTaskManager* manager) CancelableTask::CancelableTask(Isolate* isolate)
: Cancelable(manager), isolate_(isolate) {} : Cancelable(isolate->cancelable_task_manager()), isolate_(isolate) {}
CancelableIdleTask::CancelableIdleTask(Isolate* isolate) CancelableIdleTask::CancelableIdleTask(Isolate* isolate)
: CancelableIdleTask(isolate, isolate->cancelable_task_manager()) {} : Cancelable(isolate->cancelable_task_manager()), isolate_(isolate) {}
CancelableIdleTask::CancelableIdleTask(Isolate* isolate,
CancelableTaskManager* manager)
: Cancelable(manager), isolate_(isolate) {}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -126,7 +126,6 @@ class V8_EXPORT_PRIVATE Cancelable {
class CancelableTask : public Cancelable, public Task { class CancelableTask : public Cancelable, public Task {
public: public:
explicit CancelableTask(Isolate* isolate); explicit CancelableTask(Isolate* isolate);
CancelableTask(Isolate* isolate, CancelableTaskManager* manager);
// Task overrides. // Task overrides.
void Run() final { void Run() final {
@ -149,7 +148,6 @@ class CancelableTask : public Cancelable, public Task {
class CancelableIdleTask : public Cancelable, public IdleTask { class CancelableIdleTask : public Cancelable, public IdleTask {
public: public:
explicit CancelableIdleTask(Isolate* isolate); explicit CancelableIdleTask(Isolate* isolate);
CancelableIdleTask(Isolate* isolate, CancelableTaskManager* manager);
// IdleTask overrides. // IdleTask overrides.
void Run(double deadline_in_seconds) final { void Run(double deadline_in_seconds) final {

View File

@ -66,65 +66,15 @@ bool IsFinished(CompilerDispatcherJob* job) {
job->status() == CompileJobStatus::kFailed; job->status() == CompileJobStatus::kFailed;
} }
bool CanRunOnAnyThread(CompilerDispatcherJob* job) {
return (job->status() == CompileJobStatus::kReadyToParse &&
job->can_parse_on_background_thread()) ||
(job->status() == CompileJobStatus::kReadyToCompile &&
job->can_compile_on_background_thread());
}
void DoNextStepOnBackgroundThread(CompilerDispatcherJob* job) {
DCHECK(CanRunOnAnyThread(job));
switch (job->status()) {
case CompileJobStatus::kReadyToParse:
job->Parse();
break;
case CompileJobStatus::kReadyToCompile:
job->Compile();
break;
default:
UNREACHABLE();
}
}
// Theoretically we get 50ms of idle time max, however it's unlikely that // Theoretically we get 50ms of idle time max, however it's unlikely that
// we'll get all of it so try to be a conservative. // we'll get all of it so try to be a conservative.
const double kMaxIdleTimeToExpectInMs = 40; const double kMaxIdleTimeToExpectInMs = 40;
} // namespace } // namespace
class CompilerDispatcher::BackgroundTask : public CancelableTask {
public:
BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager,
CompilerDispatcher* dispatcher);
~BackgroundTask() override;
// CancelableTask implementation.
void RunInternal() override;
private:
CompilerDispatcher* dispatcher_;
DISALLOW_COPY_AND_ASSIGN(BackgroundTask);
};
CompilerDispatcher::BackgroundTask::BackgroundTask(
Isolate* isolate, CancelableTaskManager* task_manager,
CompilerDispatcher* dispatcher)
: CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {}
CompilerDispatcher::BackgroundTask::~BackgroundTask() {}
void CompilerDispatcher::BackgroundTask::RunInternal() {
dispatcher_->DoBackgroundWork();
}
class CompilerDispatcher::IdleTask : public CancelableIdleTask { class CompilerDispatcher::IdleTask : public CancelableIdleTask {
public: public:
IdleTask(Isolate* isolate, CancelableTaskManager* task_manager, IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher);
CompilerDispatcher* dispatcher);
~IdleTask() override; ~IdleTask() override;
// CancelableIdleTask implementation. // CancelableIdleTask implementation.
@ -137,9 +87,8 @@ class CompilerDispatcher::IdleTask : public CancelableIdleTask {
}; };
CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate,
CancelableTaskManager* task_manager,
CompilerDispatcher* dispatcher) CompilerDispatcher* dispatcher)
: CancelableIdleTask(isolate, task_manager), dispatcher_(dispatcher) {} : CancelableIdleTask(isolate), dispatcher_(dispatcher) {}
CompilerDispatcher::IdleTask::~IdleTask() {} CompilerDispatcher::IdleTask::~IdleTask() {}
@ -153,15 +102,11 @@ CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
platform_(platform), platform_(platform),
max_stack_size_(max_stack_size), max_stack_size_(max_stack_size),
tracer_(new CompilerDispatcherTracer(isolate_)), tracer_(new CompilerDispatcherTracer(isolate_)),
task_manager_(new CancelableTaskManager()), idle_task_scheduled_(false) {}
idle_task_scheduled_(false),
num_scheduled_background_tasks_(0),
main_thread_blocking_on_job_(nullptr) {}
CompilerDispatcher::~CompilerDispatcher() { CompilerDispatcher::~CompilerDispatcher() {
// To avoid crashing in unit tests due to unfished jobs. // To avoid crashing in unit tests due to unfished jobs.
AbortAll(BlockingBehavior::kBlock); AbortAll(BlockingBehavior::kBlock);
task_manager_->CancelAndWait();
} }
bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
@ -193,27 +138,12 @@ bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
return GetJobFor(function) != jobs_.end(); return GetJobFor(function) != jobs_.end();
} }
void CompilerDispatcher::WaitForJobIfRunningOnBackground(
CompilerDispatcherJob* job) {
base::LockGuard<base::Mutex> lock(&mutex_);
if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
pending_background_jobs_.erase(job);
return;
}
DCHECK_NULL(main_thread_blocking_on_job_);
main_thread_blocking_on_job_ = job;
while (main_thread_blocking_on_job_ != nullptr) {
main_thread_blocking_signal_.Wait(&mutex_);
}
DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end());
DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end());
}
bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
JobMap::const_iterator job = GetJobFor(function); JobMap::const_iterator job = GetJobFor(function);
CHECK(job != jobs_.end()); CHECK(job != jobs_.end());
WaitForJobIfRunningOnBackground(job->second.get()); // TODO(jochen): Check if there's an in-flight background task working on this
// job.
while (!IsFinished(job->second.get())) { while (!IsFinished(job->second.get())) {
DoNextStepOnMainThread(isolate_, job->second.get(), DoNextStepOnMainThread(isolate_, job->second.get(),
ExceptionHandling::kThrow); ExceptionHandling::kThrow);
@ -224,11 +154,23 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
return result; return result;
} }
void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function,
BlockingBehavior blocking) {
USE(blocking);
JobMap::const_iterator job = GetJobFor(function);
CHECK(job != jobs_.end());
// TODO(jochen): Check if there's an in-flight background task working on this
// job.
job->second->ResetOnMainThread();
jobs_.erase(job);
}
void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
// TODO(jochen): Implement support for non-blocking abort. USE(blocking);
DCHECK(blocking == BlockingBehavior::kBlock); // TODO(jochen): Check if there's an in-flight background task working on this
// job.
for (auto& kv : jobs_) { for (auto& kv : jobs_) {
WaitForJobIfRunningOnBackground(kv.second.get());
kv.second->ResetOnMainThread(); kv.second->ResetOnMainThread();
} }
jobs_.clear(); jobs_.clear();
@ -246,87 +188,18 @@ CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
return jobs_.end(); return jobs_.end();
} }
void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() { void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
DCHECK(platform_->IdleTasksEnabled(v8_isolate)); DCHECK(platform_->IdleTasksEnabled(v8_isolate));
{
base::LockGuard<base::Mutex> lock(&mutex_);
if (idle_task_scheduled_) return; if (idle_task_scheduled_) return;
idle_task_scheduled_ = true;
}
platform_->CallIdleOnForegroundThread(
v8_isolate, new IdleTask(isolate_, task_manager_.get(), this));
}
void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
if (jobs_.empty()) return; if (jobs_.empty()) return;
ScheduleIdleTaskFromAnyThread(); idle_task_scheduled_ = true;
} platform_->CallIdleOnForegroundThread(v8_isolate,
new IdleTask(isolate_, this));
void CompilerDispatcher::ConsiderJobForBackgroundProcessing(
CompilerDispatcherJob* job) {
if (!CanRunOnAnyThread(job)) return;
{
base::LockGuard<base::Mutex> lock(&mutex_);
pending_background_jobs_.insert(job);
}
ScheduleMoreBackgroundTasksIfNeeded();
}
void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() {
if (FLAG_single_threaded) return;
{
base::LockGuard<base::Mutex> lock(&mutex_);
if (pending_background_jobs_.empty()) return;
if (platform_->NumberOfAvailableBackgroundThreads() <=
num_scheduled_background_tasks_) {
return;
}
++num_scheduled_background_tasks_;
}
platform_->CallOnBackgroundThread(
new BackgroundTask(isolate_, task_manager_.get(), this),
v8::Platform::kShortRunningTask);
}
void CompilerDispatcher::DoBackgroundWork() {
CompilerDispatcherJob* job = nullptr;
{
base::LockGuard<base::Mutex> lock(&mutex_);
--num_scheduled_background_tasks_;
if (!pending_background_jobs_.empty()) {
auto it = pending_background_jobs_.begin();
job = *it;
pending_background_jobs_.erase(it);
running_background_jobs_.insert(job);
}
}
if (job == nullptr) return;
DoNextStepOnBackgroundThread(job);
ScheduleMoreBackgroundTasksIfNeeded();
// Unconditionally schedule an idle task, as all background steps have to be
// followed by a main thread step.
ScheduleIdleTaskFromAnyThread();
{
base::LockGuard<base::Mutex> lock(&mutex_);
running_background_jobs_.erase(job);
if (main_thread_blocking_on_job_ == job) {
main_thread_blocking_on_job_ = nullptr;
main_thread_blocking_signal_.NotifyOne();
}
}
// Don't touch |this| anymore after this point, as it might have been
// deleted.
} }
void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
{
base::LockGuard<base::Mutex> lock(&mutex_);
idle_task_scheduled_ = false; idle_task_scheduled_ = false;
}
// Number of jobs that are unlikely to make progress during any idle callback // Number of jobs that are unlikely to make progress during any idle callback
// due to their estimated duration. // due to their estimated duration.
@ -341,17 +214,6 @@ void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
job != jobs_.end() && idle_time_in_seconds > 0.0; job != jobs_.end() && idle_time_in_seconds > 0.0;
idle_time_in_seconds = idle_time_in_seconds =
deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) {
// Don't work on jobs that are being worked on by background tasks.
// Similarly, remove jobs we work on from the set of available background
// jobs.
std::unique_ptr<base::LockGuard<base::Mutex>> lock(
new base::LockGuard<base::Mutex>(&mutex_));
if (running_background_jobs_.find(job->second.get()) !=
running_background_jobs_.end()) {
++job;
continue;
}
auto it = pending_background_jobs_.find(job->second.get());
double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs();
if (idle_time_in_seconds < if (idle_time_in_seconds <
(estimate_in_ms / (estimate_in_ms /
@ -360,23 +222,14 @@ void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
// have managed to finish the job in a large idle task to assess // have managed to finish the job in a large idle task to assess
// whether we should ask for another idle callback. // whether we should ask for another idle callback.
if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs;
if (it == pending_background_jobs_.end()) {
lock.reset();
ConsiderJobForBackgroundProcessing(job->second.get());
}
++job; ++job;
} else if (IsFinished(job->second.get())) { } else if (IsFinished(job->second.get())) {
DCHECK(it == pending_background_jobs_.end());
job->second->ResetOnMainThread(); job->second->ResetOnMainThread();
job = jobs_.erase(job); job = jobs_.erase(job);
continue; break;
} else { } else {
// Do one step, and keep processing the job (as we don't advance the // Do one step, and keep processing the job (as we don't advance the
// iterator). // iterator).
if (it != pending_background_jobs_.end()) {
pending_background_jobs_.erase(it);
}
lock.reset();
DoNextStepOnMainThread(isolate_, job->second.get(), DoNextStepOnMainThread(isolate_, job->second.get(),
ExceptionHandling::kSwallow); ExceptionHandling::kSwallow);
} }

View File

@ -7,12 +7,9 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <unordered_set>
#include <utility> #include <utility>
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/globals.h" #include "src/globals.h"
#include "testing/gtest/include/gtest/gtest_prod.h" #include "testing/gtest/include/gtest/gtest_prod.h"
@ -22,7 +19,6 @@ class Platform;
namespace internal { namespace internal {
class CancelableTaskManager;
class CompilerDispatcherJob; class CompilerDispatcherJob;
class CompilerDispatcherTracer; class CompilerDispatcherTracer;
class Isolate; class Isolate;
@ -33,30 +29,6 @@ class Handle;
// The CompilerDispatcher uses a combination of idle tasks and background tasks // The CompilerDispatcher uses a combination of idle tasks and background tasks
// to parse and compile lazily parsed functions. // to parse and compile lazily parsed functions.
//
// As both parsing and compilation currently requires a preparation and
// finalization step that happens on the main thread, every task has to be
// advanced during idle time first. Depending on the properties of the task, it
// can then be parsed or compiled on either background threads, or during idle
// time. Last, it has to be finalized during idle time again.
//
// CompilerDispatcher::jobs_ maintains the list of all CompilerDispatcherJobs
// the CompilerDispatcher knows about.
//
// CompilerDispatcher::pending_background_jobs_ contains the set of
// CompilerDispatcherJobs that can be processed on a background thread.
//
// CompilerDispatcher::running_background_jobs_ contains the set of
// CompilerDispatcherJobs that are currently being processed on a background
// thread.
//
// CompilerDispatcher::DoIdleWork tries to advance as many jobs out of jobs_ as
// possible during idle time. If a job can't be advanced, but is suitable for
// background processing, it fires off background threads.
//
// CompilerDispatcher::DoBackgroundWork advances one of the pending jobs, and
// then spins of another idle task to potentially do the final step on the main
// thread.
class V8_EXPORT_PRIVATE CompilerDispatcher { class V8_EXPORT_PRIVATE CompilerDispatcher {
public: public:
enum class BlockingBehavior { kBlock, kDontBlock }; enum class BlockingBehavior { kBlock, kDontBlock };
@ -83,23 +55,15 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
private: private:
FRIEND_TEST(CompilerDispatcherTest, IdleTaskSmallIdleTime); FRIEND_TEST(CompilerDispatcherTest, IdleTaskSmallIdleTime);
FRIEND_TEST(IgnitionCompilerDispatcherTest, CompileOnBackgroundThread);
FRIEND_TEST(IgnitionCompilerDispatcherTest, FinishNowWithBackgroundTask);
typedef std::multimap<std::pair<int, int>, typedef std::multimap<std::pair<int, int>,
std::unique_ptr<CompilerDispatcherJob>> std::unique_ptr<CompilerDispatcherJob>>
JobMap; JobMap;
class BackgroundTask;
class IdleTask; class IdleTask;
void WaitForJobIfRunningOnBackground(CompilerDispatcherJob* job);
bool IsEnabled() const; bool IsEnabled() const;
JobMap::const_iterator GetJobFor(Handle<SharedFunctionInfo> shared) const; JobMap::const_iterator GetJobFor(Handle<SharedFunctionInfo> shared) const;
void ConsiderJobForBackgroundProcessing(CompilerDispatcherJob* job);
void ScheduleMoreBackgroundTasksIfNeeded();
void ScheduleIdleTaskFromAnyThread();
void ScheduleIdleTaskIfNeeded(); void ScheduleIdleTaskIfNeeded();
void DoBackgroundWork();
void DoIdleWork(double deadline_in_seconds); void DoIdleWork(double deadline_in_seconds);
Isolate* isolate_; Isolate* isolate_;
@ -107,33 +71,12 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
size_t max_stack_size_; size_t max_stack_size_;
std::unique_ptr<CompilerDispatcherTracer> tracer_; std::unique_ptr<CompilerDispatcherTracer> tracer_;
std::unique_ptr<CancelableTaskManager> task_manager_; bool idle_task_scheduled_;
// Mapping from (script id, function literal id) to job. We use a multimap, // Mapping from (script id, function literal id) to job. We use a multimap,
// as script id is not necessarily unique. // as script id is not necessarily unique.
JobMap jobs_; JobMap jobs_;
// The following members can be accessed from any thread. Methods need to hold
// the mutex |mutex_| while accessing them.
base::Mutex mutex_;
bool idle_task_scheduled_;
// Number of currently scheduled BackgroundTask objects.
size_t num_scheduled_background_tasks_;
// The set of CompilerDispatcherJobs that can be advanced on any thread.
std::unordered_set<CompilerDispatcherJob*> pending_background_jobs_;
// The set of CompilerDispatcherJobs currently processed on background
// threads.
std::unordered_set<CompilerDispatcherJob*> running_background_jobs_;
// If not nullptr, then the main thread waits for the task processing
// this job, and blocks on the ConditionVariable main_thread_blocking_signal_.
CompilerDispatcherJob* main_thread_blocking_on_job_;
base::ConditionVariable main_thread_blocking_signal_;
DISALLOW_COPY_AND_ASSIGN(CompilerDispatcher); DISALLOW_COPY_AND_ASSIGN(CompilerDispatcher);
}; };

View File

@ -5,13 +5,10 @@
#include "src/compiler-dispatcher/compiler-dispatcher.h" #include "src/compiler-dispatcher/compiler-dispatcher.h"
#include "include/v8-platform.h" #include "include/v8-platform.h"
#include "src/base/platform/semaphore.h"
#include "src/compiler-dispatcher/compiler-dispatcher-job.h" #include "src/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "src/flags.h" #include "src/flags.h"
#include "src/handles.h" #include "src/handles.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "src/v8.h"
#include "test/unittests/compiler-dispatcher/compiler-dispatcher-helper.h" #include "test/unittests/compiler-dispatcher/compiler-dispatcher-helper.h"
#include "test/unittests/test-utils.h" #include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
@ -43,44 +40,16 @@ class CompilerDispatcherTest : public TestWithContext {
bool CompilerDispatcherTest::old_flag_; bool CompilerDispatcherTest::old_flag_;
class IgnitionCompilerDispatcherTest : public CompilerDispatcherTest {
public:
IgnitionCompilerDispatcherTest() = default;
~IgnitionCompilerDispatcherTest() override = default;
static void SetUpTestCase() {
old_flag_ = i::FLAG_ignition;
i::FLAG_ignition = true;
CompilerDispatcherTest::SetUpTestCase();
}
static void TearDownTestCase() {
CompilerDispatcherTest::TearDownTestCase();
i::FLAG_ignition = old_flag_;
}
private:
static bool old_flag_;
DISALLOW_COPY_AND_ASSIGN(IgnitionCompilerDispatcherTest);
};
bool IgnitionCompilerDispatcherTest::old_flag_;
namespace { namespace {
class MockPlatform : public v8::Platform { class MockPlatform : public v8::Platform {
public: public:
MockPlatform() : idle_task_(nullptr), time_(0.0), time_step_(0.0), sem_(0) {} MockPlatform() : task_(nullptr), time_(0.0), time_step_(0.0) {}
~MockPlatform() override { ~MockPlatform() override = default;
EXPECT_TRUE(tasks_.empty());
EXPECT_TRUE(idle_task_ == nullptr);
}
size_t NumberOfAvailableBackgroundThreads() override { return 1; }
void CallOnBackgroundThread(Task* task, void CallOnBackgroundThread(Task* task,
ExpectedRuntime expected_runtime) override { ExpectedRuntime expected_runtime) override {
tasks_.push_back(task); UNREACHABLE();
} }
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
@ -94,8 +63,7 @@ class MockPlatform : public v8::Platform {
void CallIdleOnForegroundThread(v8::Isolate* isolate, void CallIdleOnForegroundThread(v8::Isolate* isolate,
IdleTask* task) override { IdleTask* task) override {
ASSERT_TRUE(idle_task_ == nullptr); task_ = task;
idle_task_ = task;
} }
bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; } bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; }
@ -106,78 +74,21 @@ class MockPlatform : public v8::Platform {
} }
void RunIdleTask(double deadline_in_seconds, double time_step) { void RunIdleTask(double deadline_in_seconds, double time_step) {
ASSERT_TRUE(idle_task_ != nullptr); ASSERT_TRUE(task_ != nullptr);
time_step_ = time_step; time_step_ = time_step;
IdleTask* task = idle_task_; IdleTask* task = task_;
idle_task_ = nullptr; task_ = nullptr;
task->Run(deadline_in_seconds); task->Run(deadline_in_seconds);
delete task; delete task;
} }
bool IdleTaskPending() const { return idle_task_; } bool IdleTaskPending() const { return !!task_; }
bool BackgroundTasksPending() const { return !tasks_.empty(); }
void RunBackgroundTasksAndBlock(Platform* platform) {
std::vector<Task*> tasks;
tasks.swap(tasks_);
platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, true),
kShortRunningTask);
sem_.Wait();
}
void RunBackgroundTasks(Platform* platform) {
std::vector<Task*> tasks;
tasks.swap(tasks_);
platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, false),
kShortRunningTask);
}
void ClearBackgroundTasks() {
std::vector<Task*> tasks;
tasks.swap(tasks_);
for (auto& task : tasks) {
delete task;
}
}
void ClearIdleTask() {
ASSERT_TRUE(idle_task_ != nullptr);
delete idle_task_;
idle_task_ = nullptr;
}
private: private:
class TaskWrapper : public Task { IdleTask* task_;
public:
TaskWrapper(MockPlatform* platform, const std::vector<Task*>& tasks,
bool signal)
: platform_(platform), tasks_(tasks), signal_(signal) {}
~TaskWrapper() = default;
void Run() override {
for (auto& task : tasks_) {
task->Run();
delete task;
}
if (signal_) platform_->sem_.Signal();
}
private:
MockPlatform* platform_;
std::vector<Task*> tasks_;
bool signal_;
DISALLOW_COPY_AND_ASSIGN(TaskWrapper);
};
IdleTask* idle_task_;
double time_; double time_;
double time_step_; double time_step_;
std::vector<Task*> tasks_;
base::Semaphore sem_;
DISALLOW_COPY_AND_ASSIGN(MockPlatform); DISALLOW_COPY_AND_ASSIGN(MockPlatform);
}; };
@ -201,10 +112,8 @@ TEST_F(CompilerDispatcherTest, IsEnqueued) {
ASSERT_FALSE(dispatcher.IsEnqueued(shared)); ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(dispatcher.Enqueue(shared)); ASSERT_TRUE(dispatcher.Enqueue(shared));
ASSERT_TRUE(dispatcher.IsEnqueued(shared)); ASSERT_TRUE(dispatcher.IsEnqueued(shared));
dispatcher.AbortAll(CompilerDispatcher::BlockingBehavior::kBlock); dispatcher.Abort(shared, CompilerDispatcher::BlockingBehavior::kBlock);
ASSERT_FALSE(dispatcher.IsEnqueued(shared)); ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(platform.IdleTaskPending());
platform.ClearIdleTask();
} }
TEST_F(CompilerDispatcherTest, FinishNow) { TEST_F(CompilerDispatcherTest, FinishNow) {
@ -223,8 +132,6 @@ TEST_F(CompilerDispatcherTest, FinishNow) {
// Finishing removes the SFI from the queue. // Finishing removes the SFI from the queue.
ASSERT_FALSE(dispatcher.IsEnqueued(shared)); ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(shared->is_compiled()); ASSERT_TRUE(shared->is_compiled());
ASSERT_TRUE(platform.IdleTaskPending());
platform.ClearIdleTask();
} }
TEST_F(CompilerDispatcherTest, IdleTask) { TEST_F(CompilerDispatcherTest, IdleTask) {
@ -281,7 +188,7 @@ TEST_F(CompilerDispatcherTest, IdleTaskSmallIdleTime) {
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kReadyToParse); CompileJobStatus::kReadyToParse);
// Now grant a lot of idle time and freeze time. // Only grant a lot of idle time and freeze time.
platform.RunIdleTask(1000.0, 0.0); platform.RunIdleTask(1000.0, 0.0);
ASSERT_FALSE(dispatcher.IsEnqueued(shared)); ASSERT_FALSE(dispatcher.IsEnqueued(shared));
@ -318,91 +225,5 @@ TEST_F(CompilerDispatcherTest, IdleTaskException) {
ASSERT_FALSE(try_catch.HasCaught()); ASSERT_FALSE(try_catch.HasCaught());
} }
TEST_F(IgnitionCompilerDispatcherTest, CompileOnBackgroundThread) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
const char script[] =
"function g() { var y = 1; function f6(x) { return x * y }; return f6; } "
"g();";
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(dispatcher.Enqueue(shared));
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kInitial);
// Make compiling super expensive, and advance job as much as possible on the
// foreground thread.
dispatcher.tracer_->RecordCompile(50000.0, 1);
platform.RunIdleTask(10.0, 0.0);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kReadyToCompile);
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
ASSERT_FALSE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.BackgroundTasksPending());
platform.RunBackgroundTasksAndBlock(V8::GetCurrentPlatform());
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.BackgroundTasksPending());
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kCompiled);
// Now grant a lot of idle time and freeze time.
platform.RunIdleTask(1000.0, 0.0);
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
}
TEST_F(IgnitionCompilerDispatcherTest, FinishNowWithBackgroundTask) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
const char script[] =
"function g() { var y = 1; function f7(x) { return x * y }; return f7; } "
"g();";
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(dispatcher.Enqueue(shared));
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kInitial);
// Make compiling super expensive, and advance job as much as possible on the
// foreground thread.
dispatcher.tracer_->RecordCompile(50000.0, 1);
platform.RunIdleTask(10.0, 0.0);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kReadyToCompile);
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
ASSERT_FALSE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.BackgroundTasksPending());
// This does not block, but races with the FinishNow() call below.
platform.RunBackgroundTasks(V8::GetCurrentPlatform());
ASSERT_TRUE(dispatcher.FinishNow(shared));
// Finishing removes the SFI from the queue.
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_FALSE(platform.BackgroundTasksPending());
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8