1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/tasks/cancelable-task.h"
6
7 #include "src/base/platform/platform.h"
8 #include "src/execution/isolate.h"
9
10 namespace v8 {
11 namespace internal {
12
~Cancelable()13 Cancelable::~Cancelable() {
14 // The following check is needed to avoid calling an already terminated
15 // manager object. This happens when the manager cancels all pending tasks
16 // in {CancelAndWait} only before destroying the manager object.
17 Status previous;
18 if (TryRun(&previous) || previous == kRunning) {
19 parent_->RemoveFinishedTask(id_);
20 }
21 }
22
CancelableTaskManager()23 CancelableTaskManager::CancelableTaskManager()
24 : task_id_counter_(kInvalidTaskId), canceled_(false) {}
25
~CancelableTaskManager()26 CancelableTaskManager::~CancelableTaskManager() {
27 // It is required that {CancelAndWait} is called before the manager object is
28 // destroyed. This guarantees that all tasks managed by this
29 // {CancelableTaskManager} are either canceled or finished their execution
30 // when the {CancelableTaskManager} dies.
31 CHECK(canceled_);
32 }
33
Register(Cancelable * task)34 CancelableTaskManager::Id CancelableTaskManager::Register(Cancelable* task) {
35 base::MutexGuard guard(&mutex_);
36 if (canceled_) {
37 // The CancelableTaskManager has already been canceled. Therefore we mark
38 // the new task immediately as canceled so that it does not get executed.
39 task->Cancel();
40 return kInvalidTaskId;
41 }
42 CancelableTaskManager::Id id = ++task_id_counter_;
43 // Id overflows are not supported.
44 CHECK_NE(kInvalidTaskId, id);
45 CHECK(!canceled_);
46 cancelable_tasks_[id] = task;
47 return id;
48 }
49
RemoveFinishedTask(CancelableTaskManager::Id id)50 void CancelableTaskManager::RemoveFinishedTask(CancelableTaskManager::Id id) {
51 CHECK_NE(kInvalidTaskId, id);
52 base::MutexGuard guard(&mutex_);
53 size_t removed = cancelable_tasks_.erase(id);
54 USE(removed);
55 DCHECK_NE(0u, removed);
56 cancelable_tasks_barrier_.NotifyOne();
57 }
58
TryAbort(CancelableTaskManager::Id id)59 TryAbortResult CancelableTaskManager::TryAbort(CancelableTaskManager::Id id) {
60 CHECK_NE(kInvalidTaskId, id);
61 base::MutexGuard guard(&mutex_);
62 auto entry = cancelable_tasks_.find(id);
63 if (entry != cancelable_tasks_.end()) {
64 Cancelable* value = entry->second;
65 if (value->Cancel()) {
66 // Cannot call RemoveFinishedTask here because of recursive locking.
67 cancelable_tasks_.erase(entry);
68 cancelable_tasks_barrier_.NotifyOne();
69 return TryAbortResult::kTaskAborted;
70 } else {
71 return TryAbortResult::kTaskRunning;
72 }
73 }
74 return TryAbortResult::kTaskRemoved;
75 }
76
CancelAndWait()77 void CancelableTaskManager::CancelAndWait() {
78 // Clean up all cancelable fore- and background tasks. Tasks are canceled on
79 // the way if possible, i.e., if they have not started yet. After each round
80 // of canceling we wait for the background tasks that have already been
81 // started.
82 base::MutexGuard guard(&mutex_);
83 canceled_ = true;
84
85 // Cancelable tasks could be running or could potentially register new
86 // tasks, requiring a loop here.
87 while (!cancelable_tasks_.empty()) {
88 for (auto it = cancelable_tasks_.begin(); it != cancelable_tasks_.end();) {
89 auto current = it;
90 // We need to get to the next element before erasing the current.
91 ++it;
92 if (current->second->Cancel()) {
93 cancelable_tasks_.erase(current);
94 }
95 }
96 // Wait for already running background tasks.
97 if (!cancelable_tasks_.empty()) {
98 cancelable_tasks_barrier_.Wait(&mutex_);
99 }
100 }
101 }
102
TryAbortAll()103 TryAbortResult CancelableTaskManager::TryAbortAll() {
104 // Clean up all cancelable fore- and background tasks. Tasks are canceled on
105 // the way if possible, i.e., if they have not started yet.
106 base::MutexGuard guard(&mutex_);
107
108 if (cancelable_tasks_.empty()) return TryAbortResult::kTaskRemoved;
109
110 for (auto it = cancelable_tasks_.begin(); it != cancelable_tasks_.end();) {
111 if (it->second->Cancel()) {
112 it = cancelable_tasks_.erase(it);
113 } else {
114 ++it;
115 }
116 }
117
118 return cancelable_tasks_.empty() ? TryAbortResult::kTaskAborted
119 : TryAbortResult::kTaskRunning;
120 }
121
CancelableTask(Isolate * isolate)122 CancelableTask::CancelableTask(Isolate* isolate)
123 : CancelableTask(isolate->cancelable_task_manager()) {}
124
CancelableTask(CancelableTaskManager * manager)125 CancelableTask::CancelableTask(CancelableTaskManager* manager)
126 : Cancelable(manager) {}
127
CancelableIdleTask(Isolate * isolate)128 CancelableIdleTask::CancelableIdleTask(Isolate* isolate)
129 : CancelableIdleTask(isolate->cancelable_task_manager()) {}
130
CancelableIdleTask(CancelableTaskManager * manager)131 CancelableIdleTask::CancelableIdleTask(CancelableTaskManager* manager)
132 : Cancelable(manager) {}
133
134 } // namespace internal
135 } // namespace v8
136