Revert "[wasm] Remove finisher task"

This reverts commit ac2fb66b65.

Reason for revert: Flakily crashes on several bots:
https://ci.chromium.org/p/v8/builders/luci.v8.ci/V8%20Win32/18524
https://ci.chromium.org/p/v8/builders/luci.v8.ci/V8%20Win64%20-%20msvc/6824
https://ci.chromium.org/p/v8/builders/luci.v8.ci/V8%20Linux64%20-%20internal%20snapshot/19766

Original change's description:
> [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}

TBR=ahaas@chromium.org,clemensh@chromium.org

Change-Id: I6b332b66adaec8f713fb31f4c8517cae7ebb4645
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: v8:7921, v8:8423
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel
Reviewed-on: https://chromium-review.googlesource.com/c/1400420
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58634}
This commit is contained in:
Michael Achenbach 2019-01-08 13:58:01 +00:00 committed by Commit Bot
parent 2dfba659dd
commit 58ca563860
9 changed files with 279 additions and 100 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()->AbortCompileJobsOnIsolate(isolate); isolate->wasm_engine()->DeleteCompileJobsOnIsolate(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()->AbortCompileJobsOnIsolate(this); wasm_engine()->DeleteCompileJobsOnIsolate(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 AbortCompilation(); void CancelAndWait();
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 AbortCompilation(); void CancelAndWait();
// 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,6 +83,9 @@ 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*);
@ -90,6 +93,10 @@ 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();
@ -207,6 +214,11 @@ 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_;
@ -233,8 +245,12 @@ 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;
@ -473,7 +489,7 @@ const CompilationStateImpl* Impl(const CompilationState* compilation_state) {
CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); } CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
void CompilationState::AbortCompilation() { Impl(this)->AbortCompilation(); } void CompilationState::CancelAndWait() { Impl(this)->CancelAndWait(); }
void CompilationState::SetError(uint32_t func_index, void CompilationState::SetError(uint32_t func_index,
const ResultBase& error_result) { const ResultBase& error_result) {
@ -660,8 +676,15 @@ 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). // foreground and background threads). The no_finisher_callback is called
// 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,
@ -698,6 +721,15 @@ 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.
@ -710,6 +742,12 @@ 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.
@ -717,6 +755,10 @@ 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);
@ -732,19 +774,38 @@ 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();
// TODO(wasm): This might already execute TurboFan units on the main thread, while (FetchAndExecuteCompilationUnit(&env, compilation_state,
// while waiting for baseline compilation to finish. This can introduce &detected_features,
// additional delay. isolate->counters()) &&
// 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()) {
FetchAndExecuteCompilationUnit(&env, compilation_state, &detected_features, // TODO(clemensh): Refactor ownership of the AsyncCompileJob and remove
isolate->counters()); // this.
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,
@ -842,6 +903,62 @@ 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:
@ -2296,18 +2413,14 @@ void AsyncCompileJob::Start() {
} }
void AsyncCompileJob::Abort() { void AsyncCompileJob::Abort() {
background_task_manager_.CancelAndWait(); // Removing this job will trigger the destructor, which will cancel all
if (native_module_) Impl(native_module_->compilation_state())->Abort(); // compilation.
// Tell the streaming decoder that compilation aborted. isolate_->wasm_engine()->RemoveCompileJob(this);
// 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(std::shared_ptr<AsyncCompileJob> job); explicit AsyncStreamingProcessor(AsyncCompileJob* job);
bool ProcessModuleHeader(Vector<const uint8_t> bytes, bool ProcessModuleHeader(Vector<const uint8_t> bytes,
uint32_t offset) override; uint32_t offset) override;
@ -2339,22 +2452,28 @@ class AsyncStreamingProcessor final : public StreamingProcessor {
void CommitCompilationUnits(); void CommitCompilationUnits();
ModuleDecoder decoder_; ModuleDecoder decoder_;
std::shared_ptr<AsyncCompileJob> job_; 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_ = std::make_shared<StreamingDecoder>( stream_.reset(
base::make_unique<AsyncStreamingProcessor>(shared_from_this())); new StreamingDecoder(base::make_unique<AsyncStreamingProcessor>(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(
@ -2424,19 +2543,19 @@ void AsyncCompileJob::FinishCompile(bool compile_wrappers) {
} }
void AsyncCompileJob::AsyncCompileFailed(Handle<Object> error_reason) { void AsyncCompileJob::AsyncCompileFailed(Handle<Object> error_reason) {
if (stream_) stream_->NotifyCompilationEnded(); // {job} keeps the {this} pointer alive.
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(std::shared_ptr<AsyncCompileJob> job) explicit CompilationStateCallback(AsyncCompileJob* job) : job_(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.
@ -2451,10 +2570,21 @@ 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_);
@ -2462,7 +2592,11 @@ class AsyncCompileJob::CompilationStateCallback {
thrower.CompileFailed(*error_result); thrower.CompileFailed(*error_result);
Handle<Object> error = thrower.Reify(); Handle<Object> error = thrower.Reify();
job_->AsyncCompileFailed(error); DeferredHandleScope deferred(job_->isolate());
error = handle(*error, job_->isolate());
job_->deferred_handles_.push_back(deferred.Detach());
job_->DoSync<CompileFailed, kUseExistingForegroundTask>(error);
} }
break; break;
@ -2475,7 +2609,7 @@ class AsyncCompileJob::CompilationStateCallback {
} }
private: private:
std::shared_ptr<AsyncCompileJob> job_; AsyncCompileJob* job_;
#ifdef DEBUG #ifdef DEBUG
base::Optional<CompilationEvent> last_event_; base::Optional<CompilationEvent> last_event_;
#endif #endif
@ -2487,7 +2621,7 @@ class AsyncCompileJob::CompileStep {
public: public:
virtual ~CompileStep() = default; virtual ~CompileStep() = default;
void Run(const std::shared_ptr<AsyncCompileJob>& job, bool on_foreground) { void Run(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_);
@ -2498,12 +2632,8 @@ class AsyncCompileJob::CompileStep {
} }
} }
virtual void RunInForeground(const std::shared_ptr<AsyncCompileJob>&) { virtual void RunInForeground(AsyncCompileJob*) { UNREACHABLE(); }
UNREACHABLE(); virtual void RunInBackground(AsyncCompileJob*) { UNREACHABLE(); }
}
virtual void RunInBackground(const std::shared_ptr<AsyncCompileJob>&) {
UNREACHABLE();
}
}; };
class AsyncCompileJob::CompileTask : public CancelableTask { class AsyncCompileJob::CompileTask : public CancelableTask {
@ -2515,7 +2645,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->shared_from_this()), job_(job),
on_foreground_(on_foreground) {} on_foreground_(on_foreground) {}
~CompileTask() override { ~CompileTask() override {
@ -2537,10 +2667,9 @@ class AsyncCompileJob::CompileTask : public CancelableTask {
} }
private: private:
// Compile tasks (foreground and background) keep the {AsyncCompileJob} alive // {job_} will be cleared to cancel a pending task.
// via shared_ptr. It will be cleared to cancel a pending task. AsyncCompileJob* job_;
std::shared_ptr<AsyncCompileJob> job_; bool on_foreground_;
const bool on_foreground_;
void ResetPendingForegroundTask() const { void ResetPendingForegroundTask() const {
DCHECK_EQ(this, job_->pending_foreground_task_); DCHECK_EQ(this, job_->pending_foreground_task_);
@ -2615,7 +2744,7 @@ class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
public: public:
explicit DecodeModule(Counters* counters) : counters_(counters) {} explicit DecodeModule(Counters* counters) : counters_(counters) {}
void RunInBackground(const std::shared_ptr<AsyncCompileJob>& job) override { void RunInBackground(AsyncCompileJob* job) override {
ModuleResult result; ModuleResult result;
{ {
DisallowHandleAllocation no_handle; DisallowHandleAllocation no_handle;
@ -2651,10 +2780,11 @@ class AsyncCompileJob::DecodeFail : public CompileStep {
private: private:
ModuleResult result_; ModuleResult result_;
void RunInForeground(const std::shared_ptr<AsyncCompileJob>& job) override { void RunInForeground(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());
} }
}; };
@ -2672,7 +2802,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(const std::shared_ptr<AsyncCompileJob>& job) override { void RunInForeground(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)
@ -2708,13 +2838,30 @@ 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(const std::shared_ptr<AsyncCompileJob>& job) override { void RunInForeground(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(
@ -2728,20 +2875,33 @@ 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(const std::shared_ptr<AsyncCompileJob>& job) override { void RunInForeground(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( AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job)
std::shared_ptr<AsyncCompileJob> job)
: decoder_(job->enabled_features_), : decoder_(job->enabled_features_),
job_(std::move(job)), job_(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.
@ -2954,14 +3114,13 @@ 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::AbortCompilation() { void CompilationStateImpl::CancelAndWait() {
background_task_manager_.CancelAndWait(); background_task_manager_.CancelAndWait();
foreground_task_manager_.CancelAndWait(); foreground_task_manager_.CancelAndWait();
} }
@ -3023,10 +3182,26 @@ 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;
@ -3136,8 +3311,19 @@ 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,11 +65,8 @@ 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 : public std::enable_shared_from_this<AsyncCompileJob> { class 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,
@ -95,6 +92,7 @@ class AsyncCompileJob : public std::enable_shared_from_this<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,10 +103,8 @@ 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.
// Move the processor out of the unique_ptr field first, to avoid recursive processor_->OnAbort();
// calls to {OnAbort}. Fail();
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_->AbortCompilation(); compilation_state_->CancelAndWait();
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(async_compile_jobs_.empty()); DCHECK(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());
std::shared_ptr<AsyncCompileJob> job = CreateAsyncCompileJob( 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) {
std::shared_ptr<AsyncCompileJob> job = 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,49 +278,48 @@ CodeTracer* WasmEngine::GetCodeTracer() {
return code_tracer_.get(); return code_tracer_.get();
} }
std::shared_ptr<AsyncCompileJob> WasmEngine::CreateAsyncCompileJob( 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) {
std::shared_ptr<AsyncCompileJob> job = AsyncCompileJob* job =
std::make_shared<AsyncCompileJob>(isolate, enabled, std::move(bytes_copy), new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
length, context, std::move(resolver)); context, std::move(resolver));
// Pass ownership to the unique_ptr in {jobs_}.
base::MutexGuard guard(&mutex_); base::MutexGuard guard(&mutex_);
async_compile_jobs_.insert({job.get(), job}); jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
return job; return job;
} }
void WasmEngine::RemoveCompileJob(AsyncCompileJob* job) { std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
AsyncCompileJob* job) {
base::MutexGuard guard(&mutex_); base::MutexGuard guard(&mutex_);
auto item = async_compile_jobs_.find(job); auto item = jobs_.find(job);
DCHECK(item != async_compile_jobs_.end()); DCHECK(item != jobs_.end());
async_compile_jobs_.erase(item); std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
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 : async_compile_jobs_) { for (auto& entry : jobs_) {
if (entry.first->isolate() == isolate) return true; if (entry.first->isolate() == isolate) return true;
} }
return false; return false;
} }
void WasmEngine::AbortCompileJobsOnIsolate(Isolate* isolate) { void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
// Copy out to a vector first, since abortion can free {AsyncCompileJob}s and base::MutexGuard guard(&mutex_);
// thus modify the {async_compile_jobs_} set. DCHECK_EQ(1, isolates_.count(isolate));
std::vector<std::shared_ptr<AsyncCompileJob>> compile_jobs_to_abort; for (auto it = jobs_.begin(); it != jobs_.end();) {
{ if (it->first->isolate() == isolate) {
base::MutexGuard guard(&mutex_); it = jobs_.erase(it);
DCHECK_EQ(1, isolates_.count(isolate)); } else {
for (auto& job_entry : async_compile_jobs_) { ++it;
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.
void RemoveCompileJob(AsyncCompileJob* job); std::unique_ptr<AsyncCompileJob> 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);
// Aborts all AsyncCompileJobs that belong to the given Isolate. All // Deletes 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 AbortCompileJobsOnIsolate(Isolate* isolate); void DeleteCompileJobsOnIsolate(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:
std::shared_ptr<AsyncCompileJob> CreateAsyncCompileJob( 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,11 +172,9 @@ class V8_EXPORT_PRIVATE WasmEngine {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Protected by {mutex_}: // Protected by {mutex_}:
// Keep weak_ptrs to the AsyncCompileJob so we can detect the intermediate // We use an AsyncCompileJob as the key for itself so that we can delete the
// state where the refcount already dropped to zero (and the weak_ptr is // job from the map when it is finished.
// cleared) but the destructor did not run to completion yet. std::unordered_map<AsyncCompileJob*, std::unique_ptr<AsyncCompileJob>> jobs_;
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_;