62fa048749
At the moment, the whole WebAssembly compilation may run in a single background task. On a low-end device, this can mean that the background thread is busy for seconds and thereby blocks other tasks, see e.g. https://crbug.com/914757. With this CL we re-schedule compilation tasks after every 50ms. These 50ms are an arbitrary number. I don't want to introduce too much overhead, but since this is in the background we also don't have to make tasks super short. Tasks which are going to compile with TurboFan will be posted with lower priority. This change requires changes in the CancelableTaskManager. At the moment it is not possible that a background task posts a new task which is managed by the same task manager as itself. The problem is about how to deal with another thread which calls CancelAndWait concurrently. At the moment, if a new task gets posted after the call to CancelAndWait, then `CHECK(!canceled_)` in CancelableTaskManager::Register will fail. If we used a lock to synchronize the calls to CancelAndWait and Register, then there would be a deadlock, where the thread which calls CancelAndWait waits for the task which wants to call Register, but at the same time blocks that task by holding the lock. With the change here, posting a task after the call to CancelAndWait will just immediately cancel the new task. This matches the behavior you would get if CancelAndWait is called right after calling Register. Bug: chromium:914757 Change-Id: I6d57aba161db8a915ec0d745658e0c28d25219a8 Reviewed-on: https://chromium-review.googlesource.com/c/1411884 Commit-Queue: Andreas Haas <ahaas@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#58898}
136 lines
4.4 KiB
C++
136 lines
4.4 KiB
C++
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "src/cancelable-task.h"
|
|
|
|
#include "src/base/platform/platform.h"
|
|
#include "src/isolate.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
Cancelable::~Cancelable() {
|
|
// The following check is needed to avoid calling an already terminated
|
|
// manager object. This happens when the manager cancels all pending tasks
|
|
// in {CancelAndWait} only before destroying the manager object.
|
|
Status previous;
|
|
if (TryRun(&previous) || previous == kRunning) {
|
|
parent_->RemoveFinishedTask(id_);
|
|
}
|
|
}
|
|
|
|
CancelableTaskManager::CancelableTaskManager()
|
|
: task_id_counter_(kInvalidTaskId), canceled_(false) {}
|
|
|
|
CancelableTaskManager::~CancelableTaskManager() {
|
|
// It is required that {CancelAndWait} is called before the manager object is
|
|
// destroyed. This guarantees that all tasks managed by this
|
|
// {CancelableTaskManager} are either canceled or finished their execution
|
|
// when the {CancelableTaskManager} dies.
|
|
CHECK(canceled_);
|
|
}
|
|
|
|
CancelableTaskManager::Id CancelableTaskManager::Register(Cancelable* task) {
|
|
base::MutexGuard guard(&mutex_);
|
|
if (canceled_) {
|
|
// The CancelableTaskManager has already been canceled. Therefore we mark
|
|
// the new task immediately as canceled so that it does not get executed.
|
|
task->Cancel();
|
|
return kInvalidTaskId;
|
|
}
|
|
CancelableTaskManager::Id id = ++task_id_counter_;
|
|
// Id overflows are not supported.
|
|
CHECK_NE(kInvalidTaskId, id);
|
|
CHECK(!canceled_);
|
|
cancelable_tasks_[id] = task;
|
|
return id;
|
|
}
|
|
|
|
void CancelableTaskManager::RemoveFinishedTask(CancelableTaskManager::Id id) {
|
|
CHECK_NE(kInvalidTaskId, id);
|
|
base::MutexGuard guard(&mutex_);
|
|
size_t removed = cancelable_tasks_.erase(id);
|
|
USE(removed);
|
|
DCHECK_NE(0u, removed);
|
|
cancelable_tasks_barrier_.NotifyOne();
|
|
}
|
|
|
|
TryAbortResult CancelableTaskManager::TryAbort(CancelableTaskManager::Id id) {
|
|
CHECK_NE(kInvalidTaskId, id);
|
|
base::MutexGuard guard(&mutex_);
|
|
auto entry = cancelable_tasks_.find(id);
|
|
if (entry != cancelable_tasks_.end()) {
|
|
Cancelable* value = entry->second;
|
|
if (value->Cancel()) {
|
|
// Cannot call RemoveFinishedTask here because of recursive locking.
|
|
cancelable_tasks_.erase(entry);
|
|
cancelable_tasks_barrier_.NotifyOne();
|
|
return TryAbortResult::kTaskAborted;
|
|
} else {
|
|
return TryAbortResult::kTaskRunning;
|
|
}
|
|
}
|
|
return TryAbortResult::kTaskRemoved;
|
|
}
|
|
|
|
void CancelableTaskManager::CancelAndWait() {
|
|
// Clean up all cancelable fore- and background tasks. Tasks are canceled on
|
|
// the way if possible, i.e., if they have not started yet. After each round
|
|
// of canceling we wait for the background tasks that have already been
|
|
// started.
|
|
base::MutexGuard guard(&mutex_);
|
|
canceled_ = true;
|
|
|
|
// Cancelable tasks could be running or could potentially register new
|
|
// tasks, requiring a loop here.
|
|
while (!cancelable_tasks_.empty()) {
|
|
for (auto it = cancelable_tasks_.begin(); it != cancelable_tasks_.end();) {
|
|
auto current = it;
|
|
// We need to get to the next element before erasing the current.
|
|
++it;
|
|
if (current->second->Cancel()) {
|
|
cancelable_tasks_.erase(current);
|
|
}
|
|
}
|
|
// Wait for already running background tasks.
|
|
if (!cancelable_tasks_.empty()) {
|
|
cancelable_tasks_barrier_.Wait(&mutex_);
|
|
}
|
|
}
|
|
}
|
|
|
|
TryAbortResult CancelableTaskManager::TryAbortAll() {
|
|
// Clean up all cancelable fore- and background tasks. Tasks are canceled on
|
|
// the way if possible, i.e., if they have not started yet.
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
if (cancelable_tasks_.empty()) return TryAbortResult::kTaskRemoved;
|
|
|
|
for (auto it = cancelable_tasks_.begin(); it != cancelable_tasks_.end();) {
|
|
if (it->second->Cancel()) {
|
|
it = cancelable_tasks_.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
|
|
return cancelable_tasks_.empty() ? TryAbortResult::kTaskAborted
|
|
: TryAbortResult::kTaskRunning;
|
|
}
|
|
|
|
CancelableTask::CancelableTask(Isolate* isolate)
|
|
: CancelableTask(isolate->cancelable_task_manager()) {}
|
|
|
|
CancelableTask::CancelableTask(CancelableTaskManager* manager)
|
|
: Cancelable(manager) {}
|
|
|
|
CancelableIdleTask::CancelableIdleTask(Isolate* isolate)
|
|
: CancelableIdleTask(isolate->cancelable_task_manager()) {}
|
|
|
|
CancelableIdleTask::CancelableIdleTask(CancelableTaskManager* manager)
|
|
: Cancelable(manager) {}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|