| Index: src/compiler-dispatcher/compiler-dispatcher.cc
|
| diff --git a/src/compiler-dispatcher/compiler-dispatcher.cc b/src/compiler-dispatcher/compiler-dispatcher.cc
|
| index 782a6ed855ea78a03f7e9829ef6f33b966e515fa..ba5154e0112591bae737245c87cf08e756c65a39 100644
|
| --- a/src/compiler-dispatcher/compiler-dispatcher.cc
|
| +++ b/src/compiler-dispatcher/compiler-dispatcher.cc
|
| @@ -52,13 +52,11 @@ bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job,
|
| break;
|
| }
|
|
|
| - if (job->status() == CompileJobStatus::kFailed) {
|
| - DCHECK(isolate->has_pending_exception());
|
| - if (exception_handling == ExceptionHandling::kSwallow) {
|
| - isolate->clear_pending_exception();
|
| - }
|
| - } else {
|
| - DCHECK(!isolate->has_pending_exception());
|
| + DCHECK_EQ(job->status() == CompileJobStatus::kFailed,
|
| + isolate->has_pending_exception());
|
| + if (job->status() == CompileJobStatus::kFailed &&
|
| + exception_handling == ExceptionHandling::kSwallow) {
|
| + isolate->clear_pending_exception();
|
| }
|
| return job->status() != CompileJobStatus::kFailed;
|
| }
|
| @@ -97,6 +95,32 @@ const double kMaxIdleTimeToExpectInMs = 40;
|
|
|
| } // namespace
|
|
|
| +class CompilerDispatcher::AbortTask : public CancelableTask {
|
| + public:
|
| + AbortTask(Isolate* isolate, CancelableTaskManager* task_manager,
|
| + CompilerDispatcher* dispatcher);
|
| + ~AbortTask() override;
|
| +
|
| + // CancelableTask implementation.
|
| + void RunInternal() override;
|
| +
|
| + private:
|
| + CompilerDispatcher* dispatcher_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(AbortTask);
|
| +};
|
| +
|
| +CompilerDispatcher::AbortTask::AbortTask(Isolate* isolate,
|
| + CancelableTaskManager* task_manager,
|
| + CompilerDispatcher* dispatcher)
|
| + : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {}
|
| +
|
| +CompilerDispatcher::AbortTask::~AbortTask() {}
|
| +
|
| +void CompilerDispatcher::AbortTask::RunInternal() {
|
| + dispatcher_->AbortInactiveJobs();
|
| +}
|
| +
|
| class CompilerDispatcher::BackgroundTask : public CancelableTask {
|
| public:
|
| BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager,
|
| @@ -156,9 +180,12 @@ CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
|
| max_stack_size_(max_stack_size),
|
| tracer_(new CompilerDispatcherTracer(isolate_)),
|
| task_manager_(new CancelableTaskManager()),
|
| + abort_(false),
|
| idle_task_scheduled_(false),
|
| num_scheduled_background_tasks_(0),
|
| - main_thread_blocking_on_job_(nullptr) {}
|
| + main_thread_blocking_on_job_(nullptr),
|
| + block_for_testing_(false),
|
| + semaphore_for_testing_(0) {}
|
|
|
| CompilerDispatcher::~CompilerDispatcher() {
|
| // To avoid crashing in unit tests due to unfished jobs.
|
| @@ -169,6 +196,11 @@ CompilerDispatcher::~CompilerDispatcher() {
|
| bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
|
| if (!IsEnabled()) return false;
|
|
|
| + {
|
| + base::LockGuard<base::Mutex> lock(&mutex_);
|
| + if (abort_) return false;
|
| + }
|
| +
|
| // We only handle functions (no eval / top-level code / wasm) that are
|
| // attached to a script.
|
| if (!function->script()->IsScript() || !function->is_function() ||
|
| @@ -223,17 +255,68 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
|
| bool result = job->second->status() != CompileJobStatus::kFailed;
|
| job->second->ResetOnMainThread();
|
| jobs_.erase(job);
|
| + if (jobs_.empty()) {
|
| + base::LockGuard<base::Mutex> lock(&mutex_);
|
| + abort_ = false;
|
| + }
|
| return result;
|
| }
|
|
|
| void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
|
| - // TODO(jochen): Implement support for non-blocking abort.
|
| - DCHECK(blocking == BlockingBehavior::kBlock);
|
| - for (auto& kv : jobs_) {
|
| - WaitForJobIfRunningOnBackground(kv.second.get());
|
| - kv.second->ResetOnMainThread();
|
| + bool background_tasks_running =
|
| + task_manager_->TryAbortAll() == CancelableTaskManager::kTaskRunning;
|
| + if (!background_tasks_running || blocking == BlockingBehavior::kBlock) {
|
| + for (auto& it : jobs_) {
|
| + WaitForJobIfRunningOnBackground(it.second.get());
|
| + it.second->ResetOnMainThread();
|
| + }
|
| + jobs_.clear();
|
| + {
|
| + base::LockGuard<base::Mutex> lock(&mutex_);
|
| + DCHECK(pending_background_jobs_.empty());
|
| + DCHECK(running_background_jobs_.empty());
|
| + abort_ = false;
|
| + }
|
| + return;
|
| + }
|
| +
|
| + {
|
| + base::LockGuard<base::Mutex> lock(&mutex_);
|
| + abort_ = true;
|
| + pending_background_jobs_.clear();
|
| + }
|
| + AbortInactiveJobs();
|
| +
|
| + // All running background jobs might already have scheduled idle tasks instead
|
| + // of abort tasks. Schedule a single abort task here to make sure they get
|
| + // processed as soon as possible (and not first when we have idle time).
|
| + ScheduleAbortTask();
|
| +}
|
| +
|
| +void CompilerDispatcher::AbortInactiveJobs() {
|
| + {
|
| + base::LockGuard<base::Mutex> lock(&mutex_);
|
| + // Since we schedule two abort tasks per async abort, we might end up
|
| + // here with nothing left to do.
|
| + if (!abort_) return;
|
| + }
|
| + for (auto it = jobs_.begin(); it != jobs_.end();) {
|
| + auto job = it;
|
| + ++it;
|
| + {
|
| + base::LockGuard<base::Mutex> lock(&mutex_);
|
| + if (running_background_jobs_.find(job->second.get()) !=
|
| + running_background_jobs_.end()) {
|
| + continue;
|
| + }
|
| + }
|
| + job->second->ResetOnMainThread();
|
| + jobs_.erase(job);
|
| + }
|
| + if (jobs_.empty()) {
|
| + base::LockGuard<base::Mutex> lock(&mutex_);
|
| + abort_ = false;
|
| }
|
| - jobs_.clear();
|
| }
|
|
|
| CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
|
| @@ -265,6 +348,12 @@ void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
|
| ScheduleIdleTaskFromAnyThread();
|
| }
|
|
|
| +void CompilerDispatcher::ScheduleAbortTask() {
|
| + v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
|
| + platform_->CallOnForegroundThread(
|
| + v8_isolate, new AbortTask(isolate_, task_manager_.get(), this));
|
| +}
|
| +
|
| void CompilerDispatcher::ConsiderJobForBackgroundProcessing(
|
| CompilerDispatcherJob* job) {
|
| if (!CanRunOnAnyThread(job)) return;
|
| @@ -304,6 +393,12 @@ void CompilerDispatcher::DoBackgroundWork() {
|
| }
|
| }
|
| if (job == nullptr) return;
|
| +
|
| + if (V8_UNLIKELY(block_for_testing_.Value())) {
|
| + block_for_testing_.SetValue(false);
|
| + semaphore_for_testing_.Wait();
|
| + }
|
| +
|
| DoNextStepOnBackgroundThread(job);
|
|
|
| ScheduleMoreBackgroundTasksIfNeeded();
|
| @@ -315,6 +410,13 @@ void CompilerDispatcher::DoBackgroundWork() {
|
| base::LockGuard<base::Mutex> lock(&mutex_);
|
| running_background_jobs_.erase(job);
|
|
|
| + if (running_background_jobs_.empty() && abort_) {
|
| + // This is the last background job that finished. The abort task
|
| + // scheduled by AbortAll might already have ran, so schedule another
|
| + // one to be on the safe side.
|
| + ScheduleAbortTask();
|
| + }
|
| +
|
| if (main_thread_blocking_on_job_ == job) {
|
| main_thread_blocking_on_job_ = nullptr;
|
| main_thread_blocking_signal_.NotifyOne();
|
| @@ -325,9 +427,16 @@ void CompilerDispatcher::DoBackgroundWork() {
|
| }
|
|
|
| void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
|
| + bool aborted = false;
|
| {
|
| base::LockGuard<base::Mutex> lock(&mutex_);
|
| idle_task_scheduled_ = false;
|
| + aborted = abort_;
|
| + }
|
| +
|
| + if (aborted) {
|
| + AbortInactiveJobs();
|
| + return;
|
| }
|
|
|
| // Number of jobs that are unlikely to make progress during any idle callback
|
|
|