Commit 27e5c0b3 authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[CompilerDispatcher] Add support for aborting a job.

Some jobs might need to be aborted, e.g., if a function is a default parameter in an
arrow function it will be re-scoped and won't have a SFI to register. Adds support to
abort jobs without having to block if the job is currently running on the background
thread.

BUG=v8:8041

Change-Id: I9149740401cbaaa31c21be9d79d4e3f5c450bfcf
Reviewed-on: https://chromium-review.googlesource.com/c/1278497
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56637}
parent f30960af
......@@ -19,7 +19,7 @@ namespace v8 {
namespace internal {
CompilerDispatcher::Job::Job(BackgroundCompileTask* task_arg)
: task(task_arg), has_run(false) {}
: task(task_arg), has_run(false), aborted(false) {}
CompilerDispatcher::Job::~Job() = default;
......@@ -168,6 +168,7 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
}
DCHECK(job->IsReadyToFinalize(&mutex_));
DCHECK(!job->aborted);
bool success = Compiler::FinalizeBackgroundCompileTask(
job->task.get(), function, isolate_, Compiler::KEEP_EXCEPTION);
......@@ -176,6 +177,24 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
return success;
}
void CompilerDispatcher::AbortJob(JobId job_id) {
if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: aborted job %zu\n", job_id);
}
JobMap::const_iterator job_it = jobs_.find(job_id);
Job* job = job_it->second.get();
base::LockGuard<base::Mutex> lock(&mutex_);
pending_background_jobs_.erase(job);
if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
RemoveJob(job_it);
} else {
// Job is currently running on the background thread, wait until it's done
// and remove job then.
job->aborted = true;
}
}
void CompilerDispatcher::AbortAll() {
task_manager_->TryAbortAll();
......@@ -318,9 +337,11 @@ void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
}
Job* job = it->second.get();
Compiler::FinalizeBackgroundCompileTask(
job->task.get(), job->function.ToHandleChecked(), isolate_,
Compiler::CLEAR_EXCEPTION);
if (!job->aborted) {
Compiler::FinalizeBackgroundCompileTask(
job->task.get(), job->function.ToHandleChecked(), isolate_,
Compiler::CLEAR_EXCEPTION);
}
RemoveJob(it);
}
......
......@@ -101,6 +101,9 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// possible). Returns true if the compile job was successful.
bool FinishNow(Handle<SharedFunctionInfo> function);
// Aborts compilation job |job_id|.
void AbortJob(JobId job_id);
// Aborts all jobs, blocking until all jobs are aborted.
void AbortAll();
......@@ -108,6 +111,8 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
FRIEND_TEST(CompilerDispatcherTest, IdleTaskNoIdleTime);
FRIEND_TEST(CompilerDispatcherTest, IdleTaskSmallIdleTime);
FRIEND_TEST(CompilerDispatcherTest, FinishNowWithWorkerTask);
FRIEND_TEST(CompilerDispatcherTest, AbortJobNotStarted);
FRIEND_TEST(CompilerDispatcherTest, AbortJobAlreadyStarted);
FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllPendingWorkerTask);
FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllRunningWorkerTask);
FRIEND_TEST(CompilerDispatcherTest, CompileMultipleOnBackgroundThread);
......@@ -117,7 +122,7 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
~Job();
bool IsReadyToFinalize(const base::MutexGuard&) {
return has_run && !function.is_null();
return aborted || (has_run && !function.is_null());
}
bool IsReadyToFinalize(base::Mutex* mutex) {
......@@ -128,6 +133,7 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
std::unique_ptr<BackgroundCompileTask> task;
MaybeHandle<SharedFunctionInfo> function;
bool has_run;
bool aborted;
};
typedef std::map<JobId, std::unique_ptr<Job>> JobMap;
......
......@@ -828,9 +828,9 @@ MaybeHandle<SharedFunctionInfo> FinalizeTopLevel(
script->FindSharedFunctionInfo(isolate, literal);
Handle<SharedFunctionInfo> shared_for_task;
if (maybe_shared_for_task.ToHandle(&shared_for_task)) {
// TODO(rmcilroy): Abort job if there is no shared function info for
// the literal.
dispatcher->RegisterSharedFunctionInfo(job_id, *shared_for_task);
} else {
dispatcher->AbortJob(job_id);
}
}
}
......
......@@ -640,6 +640,101 @@ TEST_F(CompilerDispatcherTest, FinishNowException) {
dispatcher.AbortAll();
}
TEST_F(CompilerDispatcherTest, AbortJobNotStarted) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(i_isolate(), nullptr);
ASSERT_FALSE(shared->is_compiled());
base::Optional<CompilerDispatcher::JobId> job_id =
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
ASSERT_FALSE(shared->is_compiled());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(platform.WorkerTasksPending());
dispatcher.AbortJob(*job_id);
// Aborting removes the job from the queue.
ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
ASSERT_FALSE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
platform.ClearWorkerTasks();
dispatcher.AbortAll();
}
TEST_F(CompilerDispatcherTest, AbortJobAlreadyStarted) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(i_isolate(), nullptr);
ASSERT_FALSE(shared->is_compiled());
base::Optional<CompilerDispatcher::JobId> job_id =
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
ASSERT_FALSE(shared->is_compiled());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(platform.WorkerTasksPending());
// Have dispatcher block on the background thread when running the job.
{
base::LockGuard<base::Mutex> lock(&dispatcher.mutex_);
dispatcher.block_for_testing_.SetValue(true);
}
// Start background thread and wait until it is about to run the job.
platform.RunWorkerTasks(V8::GetCurrentPlatform());
while (dispatcher.block_for_testing_.Value()) {
}
// Now abort while dispatcher is in the middle of running the job.
dispatcher.AbortJob(*job_id);
// Unblock background thread, and wait for job to complete.
{
base::LockGuard<base::Mutex> lock(&dispatcher.mutex_);
dispatcher.main_thread_blocking_on_job_ =
dispatcher.jobs_.begin()->second.get();
dispatcher.semaphore_for_testing_.Signal();
while (dispatcher.main_thread_blocking_on_job_ != nullptr) {
dispatcher.main_thread_blocking_signal_.Wait(&dispatcher.mutex_);
}
}
// Job should have finished running and then been aborted.
ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
ASSERT_FALSE(shared->is_compiled());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->aborted);
ASSERT_FALSE(platform.WorkerTasksPending());
ASSERT_TRUE(platform.IdleTaskPending());
// Runt the pending idle task
platform.RunIdleTask(1000.0, 0.0);
// Aborting removes the SFI from the queue.
ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
ASSERT_FALSE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_FALSE(platform.WorkerTasksPending());
dispatcher.AbortAll();
}
TEST_F(CompilerDispatcherTest, CompileLazyFinishesDispatcherJob) {
// Use the real dispatcher so that CompileLazy checks the same one for
// enqueued functions.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment