Commit a580b2fb authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Fix predictable mode for async compilation

In predictable mode DoSync and DoAsync are only normal
function calls. Therefore I had to do some adjustments
to async compilation to make it work with --predictable:
* I moved all calls to DoSync and DoAsync out of
  DisallowHandleAllocation and DisallowHeapAllocation
  scopes.
* I turned off the use of the semaphore which
  synchronizes the background compilation tasks with
  the main thread. It caused a deadlock.
* Adjust when the AsyncCompileJob is deleted, namely
  after the start function and not after the execution
  of the last compilation task. The reason is that in
  predictable mode all previous tasks are still on the
  stack after the last compilation task.


Bug:

Change-Id: I2f96f64febeee6b8bd5f4da3cec882797d249400
Reviewed-on: https://chromium-review.googlesource.com/469610
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44456}
parent 9ce9dde4
...@@ -2736,12 +2736,14 @@ class AsyncCompileJob { ...@@ -2736,12 +2736,14 @@ class AsyncCompileJob {
// Step 1: (async) Decode the module. // Step 1: (async) Decode the module.
//========================================================================== //==========================================================================
bool DecodeModule() { bool DecodeModule() {
DisallowHandleAllocation no_handle; {
DisallowHeapAllocation no_allocation; DisallowHandleAllocation no_handle;
// Decode the module bytes. DisallowHeapAllocation no_allocation;
TRACE_COMPILE("(1) Decoding module...\n"); // Decode the module bytes.
result_ = DecodeWasmModule(isolate_, wire_bytes_.start(), wire_bytes_.end(), TRACE_COMPILE("(1) Decoding module...\n");
true, kWasmOrigin); result_ = DecodeWasmModule(isolate_, wire_bytes_.start(),
wire_bytes_.end(), true, kWasmOrigin);
}
if (result_.failed()) { if (result_.failed()) {
// Decoding failure; reject the promise and clean up. // Decoding failure; reject the promise and clean up.
if (result_.val) delete result_.val; if (result_.val) delete result_.val;
...@@ -2812,8 +2814,7 @@ class AsyncCompileJob { ...@@ -2812,8 +2814,7 @@ class AsyncCompileJob {
isolate_->counters()->wasm_functions_per_wasm_module()->AddSample( isolate_->counters()->wasm_functions_per_wasm_module()->AddSample(
static_cast<int>(module_->functions.size())); static_cast<int>(module_->functions.size()));
helper_ = std::unique_ptr<CompilationHelper>( helper_.reset(new CompilationHelper(isolate_, module_));
new CompilationHelper(isolate_, module_));
DCHECK_LE(module_->num_imported_functions, module_->functions.size()); DCHECK_LE(module_->num_imported_functions, module_->functions.size());
size_t num_functions = size_t num_functions =
...@@ -2860,17 +2861,22 @@ class AsyncCompileJob { ...@@ -2860,17 +2861,22 @@ class AsyncCompileJob {
// Step 3 (async x K tasks): Execute compilation units. // Step 3 (async x K tasks): Execute compilation units.
//========================================================================== //==========================================================================
bool ExecuteCompilationUnits() { bool ExecuteCompilationUnits() {
DisallowHandleAllocation no_handle;
DisallowHeapAllocation no_allocation;
TRACE_COMPILE("(3) Compiling...\n"); TRACE_COMPILE("(3) Compiling...\n");
while (!failed_ && helper_->FetchAndExecuteCompilationUnit()) { while (!failed_) {
{
DisallowHandleAllocation no_handle;
DisallowHeapAllocation no_allocation;
if (!helper_->FetchAndExecuteCompilationUnit()) break;
}
// TODO(ahaas): Create one FinishCompilationUnit job for all compilation // TODO(ahaas): Create one FinishCompilationUnit job for all compilation
// units. // units.
DoSync(&AsyncCompileJob::FinishCompilationUnit); DoSync(&AsyncCompileJob::FinishCompilationUnit);
// TODO(ahaas): Limit the number of outstanding compilation units to be // TODO(ahaas): Limit the number of outstanding compilation units to be
// finished to reduce memory overhead. // finished to reduce memory overhead.
} }
helper_->module_->pending_tasks.get()->Signal(); // In predictable mode DoSync and DoAsync are only normal function calls.
// Therefore the semaphore would cause a deadlock, so we do not use it.
if (!FLAG_predictable) helper_->module_->pending_tasks.get()->Signal();
return true; return true;
} }
...@@ -2907,15 +2913,18 @@ class AsyncCompileJob { ...@@ -2907,15 +2913,18 @@ class AsyncCompileJob {
// Step 4b (async): Wait for all background tasks to finish. // Step 4b (async): Wait for all background tasks to finish.
//========================================================================== //==========================================================================
bool WaitForBackgroundTasks() { bool WaitForBackgroundTasks() {
DisallowHandleAllocation no_handle;
DisallowHeapAllocation no_allocation;
TRACE_COMPILE("(4b) Waiting for background tasks...\n"); TRACE_COMPILE("(4b) Waiting for background tasks...\n");
for (size_t i = 0; i < num_background_tasks_; ++i) { // In predictable mode DoSync and DoAsync are only normal function calls.
// If the task has not started yet, then we abort it. Otherwise we wait // Therefore the semaphore would cause a deadlock, so we do not use it.
// for it to finish. if (!FLAG_predictable) {
if (isolate_->cancelable_task_manager()->TryAbort(task_ids_.get()[i]) != for (size_t i = 0; i < num_background_tasks_; ++i) {
CancelableTaskManager::kTaskAborted) { // If the task has not started yet, then we abort it. Otherwise we wait
module_->pending_tasks.get()->Wait(); // for it to finish.
if (isolate_->cancelable_task_manager()->TryAbort(task_ids_.get()[i]) !=
CancelableTaskManager::kTaskAborted) {
module_->pending_tasks.get()->Wait();
}
} }
} }
if (failed_) { if (failed_) {
...@@ -3052,7 +3061,13 @@ class AsyncCompileJob { ...@@ -3052,7 +3061,13 @@ class AsyncCompileJob {
void RunInternal() override { void RunInternal() override {
bool more = (job_->*func_)(); // run the task. bool more = (job_->*func_)(); // run the task.
if (!more) delete job_; // if no more work, then this job is done. if (!more) {
// In predictable mode DoSync and DoAsync are only normal function
// calls. Therefore we cannot deallocate the AsyncCompilationJob here
// because all previous tasks of the compilation are still on the stack.
if (!FLAG_predictable)
delete job_; // if no more work, then this job is done.
}
} }
}; };
}; };
...@@ -3066,6 +3081,9 @@ void wasm::AsyncCompile(Isolate* isolate, Handle<JSPromise> promise, ...@@ -3066,6 +3081,9 @@ void wasm::AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
auto job = new AsyncCompileJob(isolate, std::move(copy), bytes.length(), auto job = new AsyncCompileJob(isolate, std::move(copy), bytes.length(),
handle(isolate->context()), promise); handle(isolate->context()), promise);
job->Start(); job->Start();
// In predictable mode the whole compilation takes place in Start(). Therefore
// we can just delete the code here.
if (FLAG_predictable) delete job;
} }
Handle<Code> wasm::CompileLazy(Isolate* isolate) { Handle<Code> wasm::CompileLazy(Isolate* isolate) {
......
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