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/cancelable-task.h" |
6 | |
7 | #include "src/base/platform/platform.h" |
8 | #include "src/isolate.h" |
9 | |
10 | namespace v8 { |
11 | namespace internal { |
12 | |
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 | |
23 | CancelableTaskManager::CancelableTaskManager() |
24 | : task_id_counter_(kInvalidTaskId), canceled_(false) {} |
25 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
122 | CancelableTask::CancelableTask(Isolate* isolate) |
123 | : CancelableTask(isolate->cancelable_task_manager()) {} |
124 | |
125 | CancelableTask::CancelableTask(CancelableTaskManager* manager) |
126 | : Cancelable(manager) {} |
127 | |
128 | CancelableIdleTask::CancelableIdleTask(Isolate* isolate) |
129 | : CancelableIdleTask(isolate->cancelable_task_manager()) {} |
130 | |
131 | CancelableIdleTask::CancelableIdleTask(CancelableTaskManager* manager) |
132 | : Cancelable(manager) {} |
133 | |
134 | } // namespace internal |
135 | } // namespace v8 |
136 | |