[wasm] Remove finisher task

This removes the finisher task and instead finishes compilation units
from the background.
It also changes ownership of the AsyncCompileJob to be shared among all
tasks that still operate on it. The AsyncCompileJob dies when the last
reference dies.

R=ahaas@chromium.org
CC=​​mstarzinger@chromium.org

Bug: v8:7921, v8:8423
Change-Id: Id09378327dfc146459ef41bc97176a8716756ae4
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel
Reviewed-on: https://chromium-review.googlesource.com/c/1335553
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58630}
This commit is contained in:
Clemens Hammacher 2019-01-07 17:02:21 +01:00 committed by Commit Bot
parent f9529f6b45
commit ac2fb66b65
9 changed files with 100 additions and 279 deletions

View File

@ -8629,7 +8629,7 @@ int Isolate::ContextDisposedNotification(bool dependant_context) {
if (!dependant_context) { if (!dependant_context) {
// We left the current context, we can abort all WebAssembly compilations on // We left the current context, we can abort all WebAssembly compilations on
// that isolate. // that isolate.
isolate->wasm_engine()->DeleteCompileJobsOnIsolate(isolate); isolate->wasm_engine()->AbortCompileJobsOnIsolate(isolate);
} }
// TODO(ahaas): move other non-heap activity out of the heap call. // TODO(ahaas): move other non-heap activity out of the heap call.
return isolate->heap()->NotifyContextDisposed(dependant_context); return isolate->heap()->NotifyContextDisposed(dependant_context);

View File

@ -2839,7 +2839,7 @@ void Isolate::Deinit() {
debug()->Unload(); debug()->Unload();
wasm_engine()->DeleteCompileJobsOnIsolate(this); wasm_engine()->AbortCompileJobsOnIsolate(this);
if (concurrent_recompilation_enabled()) { if (concurrent_recompilation_enabled()) {
optimizing_compile_dispatcher_->Stop(); optimizing_compile_dispatcher_->Stop();

View File

@ -93,7 +93,7 @@ class CompilationState {
using callback_t = std::function<void(CompilationEvent, const ResultBase*)>; using callback_t = std::function<void(CompilationEvent, const ResultBase*)>;
~CompilationState(); ~CompilationState();
void CancelAndWait(); void AbortCompilation();
void SetError(uint32_t func_index, const ResultBase& error_result); void SetError(uint32_t func_index, const ResultBase& error_result);

View File

@ -67,7 +67,7 @@ class CompilationStateImpl {
// Cancel all background compilation and wait for all tasks to finish. Call // Cancel all background compilation and wait for all tasks to finish. Call
// this before destructing this object. // this before destructing this object.
void CancelAndWait(); void AbortCompilation();
// Set the number of compilations unit expected to be executed. Needs to be // Set the number of compilations unit expected to be executed. Needs to be
// set before {AddCompilationUnits} is run, which triggers background // set before {AddCompilationUnits} is run, which triggers background
@ -83,9 +83,6 @@ class CompilationStateImpl {
std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units, std::vector<std::unique_ptr<WasmCompilationUnit>>& baseline_units,
std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units); std::vector<std::unique_ptr<WasmCompilationUnit>>& tiering_units);
std::unique_ptr<WasmCompilationUnit> GetNextCompilationUnit(); std::unique_ptr<WasmCompilationUnit> GetNextCompilationUnit();
std::unique_ptr<WasmCompilationUnit> GetNextExecutedUnit();
bool HasCompilationUnitToFinish();
void OnFinishedUnit(ExecutionTier); void OnFinishedUnit(ExecutionTier);
void ScheduleCodeLogging(WasmCode*); void ScheduleCodeLogging(WasmCode*);
@ -93,10 +90,6 @@ class CompilationStateImpl {
void OnBackgroundTaskStopped(const WasmFeatures& detected); void OnBackgroundTaskStopped(const WasmFeatures& detected);
void PublishDetectedFeatures(Isolate* isolate, const WasmFeatures& detected); void PublishDetectedFeatures(Isolate* isolate, const WasmFeatures& detected);
void RestartBackgroundTasks(size_t max = std::numeric_limits<size_t>::max()); void RestartBackgroundTasks(size_t max = std::numeric_limits<size_t>::max());
// Only one foreground thread (finisher) is allowed to run at a time.
// {SetFinisherIsRunning} returns whether the flag changed its state.
bool SetFinisherIsRunning(bool value);
void ScheduleFinisherTask();
void Abort(); void Abort();
@ -214,11 +207,6 @@ class CompilationStateImpl {
void NotifyOnEvent(CompilationEvent event, const VoidResult* error_result); void NotifyOnEvent(CompilationEvent event, const VoidResult* error_result);
std::vector<std::unique_ptr<WasmCompilationUnit>>& finish_units() {
return baseline_compilation_finished() ? tiering_finish_units_
: baseline_finish_units_;
}
// TODO(mstarzinger): Get rid of the Isolate field to make sure the // TODO(mstarzinger): Get rid of the Isolate field to make sure the
// {CompilationStateImpl} can be shared across multiple Isolates. // {CompilationStateImpl} can be shared across multiple Isolates.
Isolate* const isolate_; Isolate* const isolate_;
@ -245,12 +233,8 @@ class CompilationStateImpl {
std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_compilation_units_; std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_compilation_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_compilation_units_; std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_compilation_units_;
bool finisher_is_running_ = false;
size_t num_background_tasks_ = 0; size_t num_background_tasks_ = 0;
std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_finish_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_finish_units_;
// Features detected to be used in this module. Features can be detected // Features detected to be used in this module. Features can be detected
// as a module is being compiled. // as a module is being compiled.
WasmFeatures detected_features_ = kNoWasmFeatures; WasmFeatures detected_features_ = kNoWasmFeatures;
@ -489,7 +473,7 @@ const CompilationStateImpl* Impl(const CompilationState* compilation_state) {
CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); } CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
void CompilationState::CancelAndWait() { Impl(this)->CancelAndWait(); } void CompilationState::AbortCompilation() { Impl(this)->AbortCompilation(); }
void CompilationState::SetError(uint32_t func_index, void CompilationState::SetError(uint32_t func_index,
const ResultBase& error_result) { const ResultBase& error_result) {
@ -676,15 +660,8 @@ bool in_bounds(uint32_t offset, size_t size, size_t upper) {
using WasmInstanceMap = using WasmInstanceMap =
IdentityMap<Handle<WasmInstanceObject>, FreeStoreAllocationPolicy>; IdentityMap<Handle<WasmInstanceObject>, FreeStoreAllocationPolicy>;
double MonotonicallyIncreasingTimeInMs() {
return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() *
base::Time::kMillisecondsPerSecond;
}
// Run by each compilation task and by the main thread (i.e. in both // Run by each compilation task and by the main thread (i.e. in both
// foreground and background threads). The no_finisher_callback is called // foreground and background threads).
// within the result_mutex_ lock when no finishing task is running, i.e. when
// the finisher_is_running_ flag is not set.
bool FetchAndExecuteCompilationUnit(CompilationEnv* env, bool FetchAndExecuteCompilationUnit(CompilationEnv* env,
CompilationStateImpl* compilation_state, CompilationStateImpl* compilation_state,
WasmFeatures* detected, WasmFeatures* detected,
@ -721,15 +698,6 @@ void InitializeCompilationUnits(NativeModule* native_module,
builder.Commit(); builder.Commit();
} }
void FinishCompilationUnits(CompilationStateImpl* compilation_state) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "FinishCompilationUnits");
while (!compilation_state->failed()) {
std::unique_ptr<WasmCompilationUnit> unit =
compilation_state->GetNextExecutedUnit();
if (unit == nullptr) break;
}
}
void CompileInParallel(Isolate* isolate, NativeModule* native_module) { void CompileInParallel(Isolate* isolate, NativeModule* native_module) {
// Data structures for the parallel compilation. // Data structures for the parallel compilation.
@ -742,12 +710,6 @@ void CompileInParallel(Isolate* isolate, NativeModule* native_module) {
// the background threads. // the background threads.
// 2) The background threads and the main thread pick one compilation unit at // 2) The background threads and the main thread pick one compilation unit at
// a time and execute the parallel phase of the compilation unit. // a time and execute the parallel phase of the compilation unit.
// 3) After the parallel phase of all compilation units has started, the
// main thread continues to finish all compilation units as long as
// baseline-compilation units are left to be processed.
// 4) If tier-up is enabled, the main thread restarts background tasks
// that take care of compiling and finishing the top-tier compilation
// units.
// Turn on the {CanonicalHandleScope} so that the background threads can // Turn on the {CanonicalHandleScope} so that the background threads can
// use the node cache. // use the node cache.
@ -755,10 +717,6 @@ void CompileInParallel(Isolate* isolate, NativeModule* native_module) {
CompilationStateImpl* compilation_state = CompilationStateImpl* compilation_state =
Impl(native_module->compilation_state()); Impl(native_module->compilation_state());
// Make sure that no foreground task is spawned for finishing
// the compilation units. This foreground thread will be
// responsible for finishing compilation.
compilation_state->SetFinisherIsRunning(true);
uint32_t num_wasm_functions = uint32_t num_wasm_functions =
native_module->num_functions() - native_module->num_imported_functions(); native_module->num_functions() - native_module->num_imported_functions();
compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions); compilation_state->SetNumberOfFunctionsToCompile(num_wasm_functions);
@ -774,38 +732,19 @@ void CompileInParallel(Isolate* isolate, NativeModule* native_module) {
// a time and execute the parallel phase of the compilation unit. // a time and execute the parallel phase of the compilation unit.
WasmFeatures detected_features; WasmFeatures detected_features;
CompilationEnv env = native_module->CreateCompilationEnv(); CompilationEnv env = native_module->CreateCompilationEnv();
while (FetchAndExecuteCompilationUnit(&env, compilation_state, // TODO(wasm): This might already execute TurboFan units on the main thread,
&detected_features, // while waiting for baseline compilation to finish. This can introduce
isolate->counters()) && // additional delay.
// TODO(wasm): This is a busy-wait loop once all units have started executing
// in background threads. Replace by a semaphore / barrier.
while (!compilation_state->failed() &&
!compilation_state->baseline_compilation_finished()) { !compilation_state->baseline_compilation_finished()) {
// TODO(clemensh): Refactor ownership of the AsyncCompileJob and remove FetchAndExecuteCompilationUnit(&env, compilation_state, &detected_features,
// this. isolate->counters());
FinishCompilationUnits(compilation_state);
if (compilation_state->failed()) break;
}
while (!compilation_state->failed()) {
// 3) After the parallel phase of all compilation units has started, the
// main thread continues to finish compilation units as long as
// baseline compilation units are left to be processed. If compilation
// already failed, all background tasks have already been canceled
// in {FinishCompilationUnits}, and there are no units to finish.
FinishCompilationUnits(compilation_state);
if (compilation_state->baseline_compilation_finished()) break;
} }
// Publish features from the foreground and background tasks. // Publish features from the foreground and background tasks.
compilation_state->PublishDetectedFeatures(isolate, detected_features); compilation_state->PublishDetectedFeatures(isolate, detected_features);
// 4) If tiering-compilation is enabled, we need to set the finisher
// to false, such that the background threads will spawn a foreground
// thread to finish the top-tier compilation units.
if (!compilation_state->failed() &&
compilation_state->compile_mode() == CompileMode::kTiering) {
compilation_state->SetFinisherIsRunning(false);
}
} }
void CompileSequentially(Isolate* isolate, NativeModule* native_module, void CompileSequentially(Isolate* isolate, NativeModule* native_module,
@ -903,62 +842,6 @@ void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower,
} }
} }
// The runnable task that finishes compilation in foreground (e.g. updating
// the NativeModule, the code table, etc.).
class FinishCompileTask : public CancelableTask {
public:
explicit FinishCompileTask(CompilationStateImpl* compilation_state,
CancelableTaskManager* task_manager)
: CancelableTask(task_manager), compilation_state_(compilation_state) {}
void RunInternal() override {
Isolate* isolate = compilation_state_->isolate();
HandleScope scope(isolate);
SaveContext saved_context(isolate);
isolate->set_context(Context());
TRACE_COMPILE("(4a) Finishing compilation units...\n");
if (compilation_state_->failed()) {
compilation_state_->SetFinisherIsRunning(false);
return;
}
// We execute for 1 ms and then reschedule the task, same as the GC.
double deadline = MonotonicallyIncreasingTimeInMs() + 1.0;
while (true) {
compilation_state_->RestartBackgroundTasks();
std::unique_ptr<WasmCompilationUnit> unit =
compilation_state_->GetNextExecutedUnit();
if (unit == nullptr) {
// It might happen that a background task just scheduled a unit to be
// finished, but did not start a finisher task since the flag was still
// set. Check for this case, and continue if there is more work.
compilation_state_->SetFinisherIsRunning(false);
if (compilation_state_->HasCompilationUnitToFinish() &&
compilation_state_->SetFinisherIsRunning(true)) {
continue;
}
break;
}
if (compilation_state_->failed()) break;
if (deadline < MonotonicallyIncreasingTimeInMs()) {
// We reached the deadline. We reschedule this task and return
// immediately. Since we rescheduled this task already, we do not set
// the FinisherIsRunning flag to false.
compilation_state_->ScheduleFinisherTask();
return;
}
}
}
private:
CompilationStateImpl* compilation_state_;
};
// The runnable task that performs compilations in the background. // The runnable task that performs compilations in the background.
class BackgroundCompileTask : public CancelableTask { class BackgroundCompileTask : public CancelableTask {
public: public:
@ -2408,14 +2291,18 @@ void AsyncCompileJob::Start() {
} }
void AsyncCompileJob::Abort() { void AsyncCompileJob::Abort() {
// Removing this job will trigger the destructor, which will cancel all background_task_manager_.CancelAndWait();
// compilation. if (native_module_) Impl(native_module_->compilation_state())->Abort();
isolate_->wasm_engine()->RemoveCompileJob(this); // Tell the streaming decoder that compilation aborted.
// TODO(ahaas): Is this notification really necessary? Check
// https://crbug.com/888170.
if (stream_) stream_->NotifyCompilationEnded();
CancelPendingForegroundTask();
} }
class AsyncStreamingProcessor final : public StreamingProcessor { class AsyncStreamingProcessor final : public StreamingProcessor {
public: public:
explicit AsyncStreamingProcessor(AsyncCompileJob* job); explicit AsyncStreamingProcessor(std::shared_ptr<AsyncCompileJob> job);
bool ProcessModuleHeader(Vector<const uint8_t> bytes, bool ProcessModuleHeader(Vector<const uint8_t> bytes,
uint32_t offset) override; uint32_t offset) override;
@ -2447,28 +2334,22 @@ class AsyncStreamingProcessor final : public StreamingProcessor {
void CommitCompilationUnits(); void CommitCompilationUnits();
ModuleDecoder decoder_; ModuleDecoder decoder_;
AsyncCompileJob* job_; std::shared_ptr<AsyncCompileJob> job_;
std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_; std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_;
uint32_t next_function_ = 0; uint32_t next_function_ = 0;
}; };
std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() { std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
DCHECK_NULL(stream_); DCHECK_NULL(stream_);
stream_.reset( stream_ = std::make_shared<StreamingDecoder>(
new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(this))); base::make_unique<AsyncStreamingProcessor>(shared_from_this()));
return stream_; return stream_;
} }
AsyncCompileJob::~AsyncCompileJob() { AsyncCompileJob::~AsyncCompileJob() {
background_task_manager_.CancelAndWait(); background_task_manager_.CancelAndWait();
if (native_module_) Impl(native_module_->compilation_state())->Abort();
// Tell the streaming decoder that the AsyncCompileJob is not available
// anymore.
// TODO(ahaas): Is this notification really necessary? Check
// https://crbug.com/888170.
if (stream_) stream_->NotifyCompilationEnded();
CancelPendingForegroundTask();
for (auto d : deferred_handles_) delete d; for (auto d : deferred_handles_) delete d;
isolate_->wasm_engine()->RemoveCompileJob(this);
} }
void AsyncCompileJob::PrepareRuntimeObjects( void AsyncCompileJob::PrepareRuntimeObjects(
@ -2538,19 +2419,19 @@ void AsyncCompileJob::FinishCompile(bool compile_wrappers) {
} }
void AsyncCompileJob::AsyncCompileFailed(Handle<Object> error_reason) { void AsyncCompileJob::AsyncCompileFailed(Handle<Object> error_reason) {
// {job} keeps the {this} pointer alive. if (stream_) stream_->NotifyCompilationEnded();
std::shared_ptr<AsyncCompileJob> job =
isolate_->wasm_engine()->RemoveCompileJob(this);
resolver_->OnCompilationFailed(error_reason); resolver_->OnCompilationFailed(error_reason);
} }
void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) { void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
if (stream_) stream_->NotifyCompilationEnded();
resolver_->OnCompilationSucceeded(result); resolver_->OnCompilationSucceeded(result);
} }
class AsyncCompileJob::CompilationStateCallback { class AsyncCompileJob::CompilationStateCallback {
public: public:
explicit CompilationStateCallback(AsyncCompileJob* job) : job_(job) {} explicit CompilationStateCallback(std::shared_ptr<AsyncCompileJob> job)
: job_(std::move(job)) {}
void operator()(CompilationEvent event, const ResultBase* error_result) { void operator()(CompilationEvent event, const ResultBase* error_result) {
// This callback is only being called from a foreground task. // This callback is only being called from a foreground task.
@ -2565,21 +2446,10 @@ class AsyncCompileJob::CompilationStateCallback {
break; break;
case CompilationEvent::kFinishedTopTierCompilation: case CompilationEvent::kFinishedTopTierCompilation:
DCHECK_EQ(CompilationEvent::kFinishedBaselineCompilation, last_event_); DCHECK_EQ(CompilationEvent::kFinishedBaselineCompilation, last_event_);
// If a foreground task or a finisher is pending, we rely on
// FinishModule to remove the job.
if (!job_->pending_foreground_task_ &&
job_->outstanding_finishers_.load() == 0) {
job_->isolate_->wasm_engine()->RemoveCompileJob(job_);
}
break; break;
case CompilationEvent::kFailedCompilation: case CompilationEvent::kFailedCompilation:
DCHECK(!last_event_.has_value()); DCHECK(!last_event_.has_value());
DCHECK_NOT_NULL(error_result); DCHECK_NOT_NULL(error_result);
// Tier-up compilation should not fail if baseline compilation
// did not fail.
DCHECK(!Impl(job_->native_module_->compilation_state())
->baseline_compilation_finished());
{ {
SaveContext saved_context(job_->isolate()); SaveContext saved_context(job_->isolate());
job_->isolate()->set_context(*job_->native_context_); job_->isolate()->set_context(*job_->native_context_);
@ -2587,11 +2457,7 @@ class AsyncCompileJob::CompilationStateCallback {
thrower.CompileFailed(*error_result); thrower.CompileFailed(*error_result);
Handle<Object> error = thrower.Reify(); Handle<Object> error = thrower.Reify();
DeferredHandleScope deferred(job_->isolate()); job_->AsyncCompileFailed(error);
error = handle(*error, job_->isolate());
job_->deferred_handles_.push_back(deferred.Detach());
job_->DoSync<CompileFailed, kUseExistingForegroundTask>(error);
} }
break; break;
@ -2604,7 +2470,7 @@ class AsyncCompileJob::CompilationStateCallback {
} }
private: private:
AsyncCompileJob* job_; std::shared_ptr<AsyncCompileJob> job_;
#ifdef DEBUG #ifdef DEBUG
base::Optional<CompilationEvent> last_event_; base::Optional<CompilationEvent> last_event_;
#endif #endif
@ -2616,7 +2482,7 @@ class AsyncCompileJob::CompileStep {
public: public:
virtual ~CompileStep() = default; virtual ~CompileStep() = default;
void Run(AsyncCompileJob* job, bool on_foreground) { void Run(const std::shared_ptr<AsyncCompileJob>& job, bool on_foreground) {
if (on_foreground) { if (on_foreground) {
HandleScope scope(job->isolate_); HandleScope scope(job->isolate_);
SaveContext saved_context(job->isolate_); SaveContext saved_context(job->isolate_);
@ -2627,8 +2493,12 @@ class AsyncCompileJob::CompileStep {
} }
} }
virtual void RunInForeground(AsyncCompileJob*) { UNREACHABLE(); } virtual void RunInForeground(const std::shared_ptr<AsyncCompileJob>&) {
virtual void RunInBackground(AsyncCompileJob*) { UNREACHABLE(); } UNREACHABLE();
}
virtual void RunInBackground(const std::shared_ptr<AsyncCompileJob>&) {
UNREACHABLE();
}
}; };
class AsyncCompileJob::CompileTask : public CancelableTask { class AsyncCompileJob::CompileTask : public CancelableTask {
@ -2640,7 +2510,7 @@ class AsyncCompileJob::CompileTask : public CancelableTask {
// their own task manager. // their own task manager.
: CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager() : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager()
: &job->background_task_manager_), : &job->background_task_manager_),
job_(job), job_(job->shared_from_this()),
on_foreground_(on_foreground) {} on_foreground_(on_foreground) {}
~CompileTask() override { ~CompileTask() override {
@ -2662,9 +2532,10 @@ class AsyncCompileJob::CompileTask : public CancelableTask {
} }
private: private:
// {job_} will be cleared to cancel a pending task. // Compile tasks (foreground and background) keep the {AsyncCompileJob} alive
AsyncCompileJob* job_; // via shared_ptr. It will be cleared to cancel a pending task.
bool on_foreground_; std::shared_ptr<AsyncCompileJob> job_;
const bool on_foreground_;
void ResetPendingForegroundTask() const { void ResetPendingForegroundTask() const {
DCHECK_EQ(this, job_->pending_foreground_task_); DCHECK_EQ(this, job_->pending_foreground_task_);
@ -2739,7 +2610,7 @@ class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
public: public:
explicit DecodeModule(Counters* counters) : counters_(counters) {} explicit DecodeModule(Counters* counters) : counters_(counters) {}
void RunInBackground(AsyncCompileJob* job) override { void RunInBackground(const std::shared_ptr<AsyncCompileJob>& job) override {
ModuleResult result; ModuleResult result;
{ {
DisallowHandleAllocation no_handle; DisallowHandleAllocation no_handle;
@ -2775,11 +2646,10 @@ class AsyncCompileJob::DecodeFail : public CompileStep {
private: private:
ModuleResult result_; ModuleResult result_;
void RunInForeground(AsyncCompileJob* job) override { void RunInForeground(const std::shared_ptr<AsyncCompileJob>& job) override {
TRACE_COMPILE("(1b) Decoding failed.\n"); TRACE_COMPILE("(1b) Decoding failed.\n");
ErrorThrower thrower(job->isolate_, "AsyncCompile"); ErrorThrower thrower(job->isolate_, "AsyncCompile");
thrower.CompileFailed("Wasm decoding failed", result_); thrower.CompileFailed("Wasm decoding failed", result_);
// {job_} is deleted in AsyncCompileFailed, therefore the {return}.
return job->AsyncCompileFailed(thrower.Reify()); return job->AsyncCompileFailed(thrower.Reify());
} }
}; };
@ -2797,7 +2667,7 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
std::shared_ptr<const WasmModule> module_; std::shared_ptr<const WasmModule> module_;
bool start_compilation_; bool start_compilation_;
void RunInForeground(AsyncCompileJob* job) override { void RunInForeground(const std::shared_ptr<AsyncCompileJob>& job) override {
TRACE_COMPILE("(2) Prepare and start compile...\n"); TRACE_COMPILE("(2) Prepare and start compile...\n");
// Make sure all compilation tasks stopped running. Decoding (async step) // Make sure all compilation tasks stopped running. Decoding (async step)
@ -2833,30 +2703,13 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
} }
}; };
//==========================================================================
// Step 4b (sync): Compilation failed. Reject Promise.
//==========================================================================
class AsyncCompileJob::CompileFailed : public CompileStep {
public:
explicit CompileFailed(Handle<Object> error_reason)
: error_reason_(error_reason) {}
void RunInForeground(AsyncCompileJob* job) override {
TRACE_COMPILE("(4b) Compilation Failed...\n");
return job->AsyncCompileFailed(error_reason_);
}
private:
Handle<Object> error_reason_;
};
//========================================================================== //==========================================================================
// Step 5 (sync): Compile JS->wasm wrappers. // Step 5 (sync): Compile JS->wasm wrappers.
//========================================================================== //==========================================================================
class AsyncCompileJob::CompileWrappers : public CompileStep { class AsyncCompileJob::CompileWrappers : public CompileStep {
// TODO(wasm): Compile all wrappers here, including the start function wrapper // TODO(wasm): Compile all wrappers here, including the start function wrapper
// and the wrappers for the function table elements. // and the wrappers for the function table elements.
void RunInForeground(AsyncCompileJob* job) override { void RunInForeground(const std::shared_ptr<AsyncCompileJob>& job) override {
TRACE_COMPILE("(5) Compile wrappers...\n"); TRACE_COMPILE("(5) Compile wrappers...\n");
// Compile JS->wasm wrappers for exported functions. // Compile JS->wasm wrappers for exported functions.
CompileJsToWasmWrappers( CompileJsToWasmWrappers(
@ -2870,33 +2723,20 @@ class AsyncCompileJob::CompileWrappers : public CompileStep {
// Step 6 (sync): Finish the module and resolve the promise. // Step 6 (sync): Finish the module and resolve the promise.
//========================================================================== //==========================================================================
class AsyncCompileJob::FinishModule : public CompileStep { class AsyncCompileJob::FinishModule : public CompileStep {
void RunInForeground(AsyncCompileJob* job) override { void RunInForeground(const std::shared_ptr<AsyncCompileJob>& job) override {
TRACE_COMPILE("(6) Finish module...\n"); TRACE_COMPILE("(6) Finish module...\n");
job->AsyncCompileSucceeded(job->module_object_); job->AsyncCompileSucceeded(job->module_object_);
size_t num_functions = job->native_module_->num_functions() -
job->native_module_->num_imported_functions();
auto* compilation_state = Impl(job->native_module_->compilation_state());
if (compilation_state->compile_mode() == CompileMode::kRegular ||
num_functions == 0) {
// If we do not tier up, the async compile job is done here and
// can be deleted.
job->isolate_->wasm_engine()->RemoveCompileJob(job);
return;
}
DCHECK_EQ(CompileMode::kTiering, compilation_state->compile_mode());
if (compilation_state->baseline_compilation_finished()) {
job->isolate_->wasm_engine()->RemoveCompileJob(job);
}
} }
}; };
AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job) AsyncStreamingProcessor::AsyncStreamingProcessor(
std::shared_ptr<AsyncCompileJob> job)
: decoder_(job->enabled_features_), : decoder_(job->enabled_features_),
job_(job), job_(std::move(job)),
compilation_unit_builder_(nullptr) {} compilation_unit_builder_(nullptr) {}
void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(ResultBase error) { void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(ResultBase error) {
TRACE_STREAMING("Finish streaming with error.\n");
DCHECK(error.failed()); DCHECK(error.failed());
// Make sure all background tasks stopped executing before we change the state // Make sure all background tasks stopped executing before we change the state
// of the AsyncCompileJob to DecodeFail. // of the AsyncCompileJob to DecodeFail.
@ -3109,13 +2949,14 @@ CompilationStateImpl::CompilationStateImpl(internal::Isolate* isolate,
} }
CompilationStateImpl::~CompilationStateImpl() { CompilationStateImpl::~CompilationStateImpl() {
// {AbortCompilation} must have been called.
DCHECK(background_task_manager_.canceled()); DCHECK(background_task_manager_.canceled());
DCHECK(foreground_task_manager_.canceled()); DCHECK(foreground_task_manager_.canceled());
CompilationError* error = compile_error_.load(std::memory_order_acquire); CompilationError* error = compile_error_.load(std::memory_order_acquire);
if (error != nullptr) delete error; if (error != nullptr) delete error;
} }
void CompilationStateImpl::CancelAndWait() { void CompilationStateImpl::AbortCompilation() {
background_task_manager_.CancelAndWait(); background_task_manager_.CancelAndWait();
foreground_task_manager_.CancelAndWait(); foreground_task_manager_.CancelAndWait();
} }
@ -3177,26 +3018,10 @@ CompilationStateImpl::GetNextCompilationUnit() {
return std::unique_ptr<WasmCompilationUnit>(); return std::unique_ptr<WasmCompilationUnit>();
} }
std::unique_ptr<WasmCompilationUnit>
CompilationStateImpl::GetNextExecutedUnit() {
std::vector<std::unique_ptr<WasmCompilationUnit>>& units = finish_units();
base::MutexGuard guard(&mutex_);
if (units.empty()) return {};
std::unique_ptr<WasmCompilationUnit> ret = std::move(units.back());
units.pop_back();
return ret;
}
bool CompilationStateImpl::HasCompilationUnitToFinish() {
return !finish_units().empty();
}
void CompilationStateImpl::OnFinishedUnit(ExecutionTier tier) { void CompilationStateImpl::OnFinishedUnit(ExecutionTier tier) {
// This mutex guarantees that events happen in the right order. // This mutex guarantees that events happen in the right order.
base::MutexGuard guard(&mutex_); base::MutexGuard guard(&mutex_);
if (failed()) return;
// If we are *not* compiling in tiering mode, then all units are counted as // If we are *not* compiling in tiering mode, then all units are counted as
// baseline units. // baseline units.
bool is_tiering_mode = compile_mode_ == CompileMode::kTiering; bool is_tiering_mode = compile_mode_ == CompileMode::kTiering;
@ -3306,19 +3131,8 @@ void CompilationStateImpl::RestartBackgroundTasks(size_t max) {
} }
} }
bool CompilationStateImpl::SetFinisherIsRunning(bool value) {
base::MutexGuard guard(&mutex_);
if (finisher_is_running_ == value) return false;
finisher_is_running_ = value;
return true;
}
void CompilationStateImpl::ScheduleFinisherTask() {
foreground_task_runner_->PostTask(
base::make_unique<FinishCompileTask>(this, &foreground_task_manager_));
}
void CompilationStateImpl::Abort() { void CompilationStateImpl::Abort() {
printf("Abort: %p\n", this);
SetError(0, VoidResult::Error(0, "Compilation aborted")); SetError(0, VoidResult::Error(0, "Compilation aborted"));
background_task_manager_.CancelAndWait(); background_task_manager_.CancelAndWait();
// No more callbacks after abort. Don't free the std::function objects here, // No more callbacks after abort. Don't free the std::function objects here,

View File

@ -65,8 +65,11 @@ Address CompileLazy(Isolate*, NativeModule*, uint32_t func_index);
// allocates on the V8 heap (e.g. creating the module object) must be a // allocates on the V8 heap (e.g. creating the module object) must be a
// foreground task. All other tasks (e.g. decoding and validating, the majority // foreground task. All other tasks (e.g. decoding and validating, the majority
// of the work of compilation) can be background tasks. // of the work of compilation) can be background tasks.
// AsyncCompileJobs are stored in shared_ptr by all tasks which need to keep
// them alive. {std::enable_shared_from_this} allows to regain a shared_ptr from
// a raw ptr.
// TODO(wasm): factor out common parts of this with the synchronous pipeline. // TODO(wasm): factor out common parts of this with the synchronous pipeline.
class AsyncCompileJob { class AsyncCompileJob : public std::enable_shared_from_this<AsyncCompileJob> {
public: public:
AsyncCompileJob(Isolate* isolate, const WasmFeatures& enabled_features, AsyncCompileJob(Isolate* isolate, const WasmFeatures& enabled_features,
std::unique_ptr<byte[]> bytes_copy, size_t length, std::unique_ptr<byte[]> bytes_copy, size_t length,
@ -92,7 +95,6 @@ class AsyncCompileJob {
class DecodeModule; // Step 1 (async) class DecodeModule; // Step 1 (async)
class DecodeFail; // Step 1b (sync) class DecodeFail; // Step 1b (sync)
class PrepareAndStartCompile; // Step 2 (sync) class PrepareAndStartCompile; // Step 2 (sync)
class CompileFailed; // Step 4b (sync)
class CompileWrappers; // Step 5 (sync) class CompileWrappers; // Step 5 (sync)
class FinishModule; // Step 6 (sync) class FinishModule; // Step 6 (sync)

View File

@ -103,8 +103,10 @@ void StreamingDecoder::Finish() {
void StreamingDecoder::Abort() { void StreamingDecoder::Abort() {
TRACE_STREAMING("Abort\n"); TRACE_STREAMING("Abort\n");
if (!ok()) return; // Failed already. if (!ok()) return; // Failed already.
processor_->OnAbort(); // Move the processor out of the unique_ptr field first, to avoid recursive
Fail(); // calls to {OnAbort}.
std::unique_ptr<StreamingProcessor> processor = std::move(processor_);
processor->OnAbort();
} }
void StreamingDecoder::SetModuleCompiledCallback( void StreamingDecoder::SetModuleCompiledCallback(

View File

@ -869,7 +869,7 @@ NativeModule::~NativeModule() {
TRACE_HEAP("Deleting native module: %p\n", reinterpret_cast<void*>(this)); TRACE_HEAP("Deleting native module: %p\n", reinterpret_cast<void*>(this));
// Cancel all background compilation before resetting any field of the // Cancel all background compilation before resetting any field of the
// NativeModule or freeing anything. // NativeModule or freeing anything.
compilation_state_->CancelAndWait(); compilation_state_->AbortCompilation();
code_manager_->FreeNativeModule(this); code_manager_->FreeNativeModule(this);
} }

View File

@ -24,7 +24,7 @@ WasmEngine::WasmEngine()
WasmEngine::~WasmEngine() { WasmEngine::~WasmEngine() {
// All AsyncCompileJobs have been canceled. // All AsyncCompileJobs have been canceled.
DCHECK(jobs_.empty()); DCHECK(async_compile_jobs_.empty());
// All Isolates have been deregistered. // All Isolates have been deregistered.
DCHECK(isolates_.empty()); DCHECK(isolates_.empty());
} }
@ -212,7 +212,7 @@ void WasmEngine::AsyncCompile(
std::unique_ptr<byte[]> copy(new byte[bytes.length()]); std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
memcpy(copy.get(), bytes.start(), bytes.length()); memcpy(copy.get(), bytes.start(), bytes.length());
AsyncCompileJob* job = CreateAsyncCompileJob( std::shared_ptr<AsyncCompileJob> job = CreateAsyncCompileJob(
isolate, enabled, std::move(copy), bytes.length(), isolate, enabled, std::move(copy), bytes.length(),
handle(isolate->context(), isolate), std::move(resolver)); handle(isolate->context(), isolate), std::move(resolver));
job->Start(); job->Start();
@ -221,7 +221,7 @@ void WasmEngine::AsyncCompile(
std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation( std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context, Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
std::shared_ptr<CompilationResultResolver> resolver) { std::shared_ptr<CompilationResultResolver> resolver) {
AsyncCompileJob* job = std::shared_ptr<AsyncCompileJob> job =
CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr), CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
0, context, std::move(resolver)); 0, context, std::move(resolver));
return job->CreateStreamingDecoder(); return job->CreateStreamingDecoder();
@ -278,48 +278,49 @@ CodeTracer* WasmEngine::GetCodeTracer() {
return code_tracer_.get(); return code_tracer_.get();
} }
AsyncCompileJob* WasmEngine::CreateAsyncCompileJob( std::shared_ptr<AsyncCompileJob> WasmEngine::CreateAsyncCompileJob(
Isolate* isolate, const WasmFeatures& enabled, Isolate* isolate, const WasmFeatures& enabled,
std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context, std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
std::shared_ptr<CompilationResultResolver> resolver) { std::shared_ptr<CompilationResultResolver> resolver) {
AsyncCompileJob* job = std::shared_ptr<AsyncCompileJob> job =
new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length, std::make_shared<AsyncCompileJob>(isolate, enabled, std::move(bytes_copy),
context, std::move(resolver)); length, context, std::move(resolver));
// Pass ownership to the unique_ptr in {jobs_}.
base::MutexGuard guard(&mutex_); base::MutexGuard guard(&mutex_);
jobs_[job] = std::unique_ptr<AsyncCompileJob>(job); async_compile_jobs_.insert({job.get(), job});
return job; return job;
} }
std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob( void WasmEngine::RemoveCompileJob(AsyncCompileJob* job) {
AsyncCompileJob* job) {
base::MutexGuard guard(&mutex_); base::MutexGuard guard(&mutex_);
auto item = jobs_.find(job); auto item = async_compile_jobs_.find(job);
DCHECK(item != jobs_.end()); DCHECK(item != async_compile_jobs_.end());
std::unique_ptr<AsyncCompileJob> result = std::move(item->second); async_compile_jobs_.erase(item);
jobs_.erase(item);
return result;
} }
bool WasmEngine::HasRunningCompileJob(Isolate* isolate) { bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
base::MutexGuard guard(&mutex_); base::MutexGuard guard(&mutex_);
DCHECK_EQ(1, isolates_.count(isolate)); DCHECK_EQ(1, isolates_.count(isolate));
for (auto& entry : jobs_) { for (auto& entry : async_compile_jobs_) {
if (entry.first->isolate() == isolate) return true; if (entry.first->isolate() == isolate) return true;
} }
return false; return false;
} }
void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) { void WasmEngine::AbortCompileJobsOnIsolate(Isolate* isolate) {
base::MutexGuard guard(&mutex_); // Copy out to a vector first, since abortion can free {AsyncCompileJob}s and
DCHECK_EQ(1, isolates_.count(isolate)); // thus modify the {async_compile_jobs_} set.
for (auto it = jobs_.begin(); it != jobs_.end();) { std::vector<std::shared_ptr<AsyncCompileJob>> compile_jobs_to_abort;
if (it->first->isolate() == isolate) { {
it = jobs_.erase(it); base::MutexGuard guard(&mutex_);
} else { DCHECK_EQ(1, isolates_.count(isolate));
++it; for (auto& job_entry : async_compile_jobs_) {
if (job_entry.first->isolate() != isolate) continue;
if (auto shared_job = job_entry.second.lock()) {
compile_jobs_to_abort.emplace_back(std::move(shared_job));
}
} }
} }
for (auto job : compile_jobs_to_abort) job->Abort();
} }
void WasmEngine::AddIsolate(Isolate* isolate) { void WasmEngine::AddIsolate(Isolate* isolate) {

View File

@ -131,16 +131,16 @@ class V8_EXPORT_PRIVATE WasmEngine {
CodeTracer* GetCodeTracer(); CodeTracer* GetCodeTracer();
// Remove {job} from the list of active compile jobs. // Remove {job} from the list of active compile jobs.
std::unique_ptr<AsyncCompileJob> RemoveCompileJob(AsyncCompileJob* job); void RemoveCompileJob(AsyncCompileJob* job);
// Returns true if at least one AsyncCompileJob that belongs to the given // Returns true if at least one AsyncCompileJob that belongs to the given
// Isolate is currently running. // Isolate is currently running.
bool HasRunningCompileJob(Isolate* isolate); bool HasRunningCompileJob(Isolate* isolate);
// Deletes all AsyncCompileJobs that belong to the given Isolate. All // Aborts all AsyncCompileJobs that belong to the given Isolate. All
// compilation is aborted, no more callbacks will be triggered. This is used // compilation is aborted, no more callbacks will be triggered. This is used
// for tearing down an isolate, or to clean it up to be reused. // for tearing down an isolate, or to clean it up to be reused.
void DeleteCompileJobsOnIsolate(Isolate* isolate); void AbortCompileJobsOnIsolate(Isolate* isolate);
// Manage the set of Isolates that use this WasmEngine. // Manage the set of Isolates that use this WasmEngine.
void AddIsolate(Isolate* isolate); void AddIsolate(Isolate* isolate);
@ -155,7 +155,7 @@ class V8_EXPORT_PRIVATE WasmEngine {
static std::shared_ptr<WasmEngine> GetWasmEngine(); static std::shared_ptr<WasmEngine> GetWasmEngine();
private: private:
AsyncCompileJob* CreateAsyncCompileJob( std::shared_ptr<AsyncCompileJob> CreateAsyncCompileJob(
Isolate* isolate, const WasmFeatures& enabled, Isolate* isolate, const WasmFeatures& enabled,
std::unique_ptr<byte[]> bytes_copy, size_t length, std::unique_ptr<byte[]> bytes_copy, size_t length,
Handle<Context> context, Handle<Context> context,
@ -172,9 +172,11 @@ class V8_EXPORT_PRIVATE WasmEngine {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Protected by {mutex_}: // Protected by {mutex_}:
// We use an AsyncCompileJob as the key for itself so that we can delete the // Keep weak_ptrs to the AsyncCompileJob so we can detect the intermediate
// job from the map when it is finished. // state where the refcount already dropped to zero (and the weak_ptr is
std::unordered_map<AsyncCompileJob*, std::unique_ptr<AsyncCompileJob>> jobs_; // cleared) but the destructor did not run to completion yet.
std::unordered_map<AsyncCompileJob*, std::weak_ptr<AsyncCompileJob>>
async_compile_jobs_;
std::unique_ptr<CompilationStatistics> compilation_stats_; std::unique_ptr<CompilationStatistics> compilation_stats_;
std::unique_ptr<CodeTracer> code_tracer_; std::unique_ptr<CodeTracer> code_tracer_;