Line data Source code
1 : // Copyright 2018 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/microtask-queue.h"
6 :
7 : #include <stddef.h>
8 : #include <algorithm>
9 :
10 : #include "src/api-inl.h"
11 : #include "src/base/logging.h"
12 : #include "src/handles-inl.h"
13 : #include "src/isolate.h"
14 : #include "src/objects/microtask-inl.h"
15 : #include "src/roots-inl.h"
16 : #include "src/tracing/trace-event.h"
17 : #include "src/visitors.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 :
22 : const size_t MicrotaskQueue::kRingBufferOffset =
23 : OFFSET_OF(MicrotaskQueue, ring_buffer_);
24 : const size_t MicrotaskQueue::kCapacityOffset =
25 : OFFSET_OF(MicrotaskQueue, capacity_);
26 : const size_t MicrotaskQueue::kSizeOffset = OFFSET_OF(MicrotaskQueue, size_);
27 : const size_t MicrotaskQueue::kStartOffset = OFFSET_OF(MicrotaskQueue, start_);
28 : const size_t MicrotaskQueue::kFinishedMicrotaskCountOffset =
29 : OFFSET_OF(MicrotaskQueue, finished_microtask_count_);
30 :
31 : const intptr_t MicrotaskQueue::kMinimumCapacity = 8;
32 :
33 : // static
34 62440 : void MicrotaskQueue::SetUpDefaultMicrotaskQueue(Isolate* isolate) {
35 : DCHECK_NULL(isolate->default_microtask_queue());
36 :
37 62440 : MicrotaskQueue* microtask_queue = new MicrotaskQueue;
38 62440 : microtask_queue->next_ = microtask_queue;
39 62440 : microtask_queue->prev_ = microtask_queue;
40 : isolate->set_default_microtask_queue(microtask_queue);
41 62440 : }
42 :
43 : // static
44 15 : std::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate) {
45 : DCHECK_NOT_NULL(isolate->default_microtask_queue());
46 :
47 15 : std::unique_ptr<MicrotaskQueue> microtask_queue(new MicrotaskQueue);
48 :
49 : // Insert the new instance to the next of last MicrotaskQueue instance.
50 15 : MicrotaskQueue* last = isolate->default_microtask_queue()->prev_;
51 15 : microtask_queue->next_ = last->next_;
52 15 : microtask_queue->prev_ = last;
53 15 : last->next_->prev_ = microtask_queue.get();
54 15 : last->next_ = microtask_queue.get();
55 :
56 15 : return microtask_queue;
57 : }
58 :
59 : MicrotaskQueue::MicrotaskQueue() = default;
60 :
61 187326 : MicrotaskQueue::~MicrotaskQueue() {
62 62442 : if (next_ != this) {
63 : DCHECK_NE(prev_, this);
64 15 : next_->prev_ = prev_;
65 15 : prev_->next_ = next_;
66 : }
67 62442 : delete[] ring_buffer_;
68 124884 : }
69 :
70 : // static
71 2720 : Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
72 : intptr_t microtask_queue_pointer,
73 : Address raw_microtask) {
74 2720 : Microtask microtask = Microtask::cast(Object(raw_microtask));
75 : reinterpret_cast<MicrotaskQueue*>(microtask_queue_pointer)
76 2720 : ->EnqueueMicrotask(microtask);
77 2720 : return ReadOnlyRoots(isolate).undefined_value().ptr();
78 : }
79 :
80 120 : void MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
81 : v8::Local<Function> function) {
82 : Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
83 : HandleScope scope(isolate);
84 : Handle<CallableTask> microtask = isolate->factory()->NewCallableTask(
85 240 : Utils::OpenHandle(*function), isolate->native_context());
86 120 : EnqueueMicrotask(*microtask);
87 120 : }
88 :
89 0 : void MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
90 : v8::MicrotaskCallback callback,
91 : void* data) {
92 : Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
93 : HandleScope scope(isolate);
94 : Handle<CallbackTask> microtask = isolate->factory()->NewCallbackTask(
95 : isolate->factory()->NewForeign(reinterpret_cast<Address>(callback)),
96 0 : isolate->factory()->NewForeign(reinterpret_cast<Address>(data)));
97 0 : EnqueueMicrotask(*microtask);
98 0 : }
99 :
100 14092 : void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
101 14092 : if (size_ == capacity_) {
102 : // Keep the capacity of |ring_buffer_| power of 2, so that the JIT
103 : // implementation can calculate the modulo easily.
104 6370 : intptr_t new_capacity = std::max(kMinimumCapacity, capacity_ << 1);
105 3185 : ResizeBuffer(new_capacity);
106 : }
107 :
108 : DCHECK_LT(size_, capacity_);
109 28184 : ring_buffer_[(start_ + size_) % capacity_] = microtask.ptr();
110 14092 : ++size_;
111 14092 : }
112 :
113 120970 : void MicrotaskQueue::PerformCheckpoint(v8::Isolate* v8_isolate) {
114 241907 : if (!IsRunningMicrotasks() && !GetMicrotasksScopeDepth() &&
115 : !HasMicrotasksSuppressions()) {
116 : Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
117 119220 : RunMicrotasks(isolate);
118 : }
119 120970 : }
120 :
121 : namespace {
122 :
123 : class SetIsRunningMicrotasks {
124 : public:
125 : explicit SetIsRunningMicrotasks(bool* flag) : flag_(flag) {
126 : DCHECK(!*flag_);
127 38068 : *flag_ = true;
128 : }
129 :
130 : ~SetIsRunningMicrotasks() {
131 : DCHECK(*flag_);
132 38068 : *flag_ = false;
133 : }
134 :
135 : private:
136 : bool* flag_;
137 : };
138 :
139 : } // namespace
140 :
141 121391 : int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
142 121391 : if (!size()) {
143 : OnCompleted(isolate);
144 83321 : return 0;
145 : }
146 :
147 38068 : intptr_t base_count = finished_microtask_count_;
148 :
149 : HandleScope handle_scope(isolate);
150 38068 : MaybeHandle<Object> maybe_exception;
151 :
152 : MaybeHandle<Object> maybe_result;
153 :
154 : int processed_microtask_count;
155 : {
156 38068 : SetIsRunningMicrotasks scope(&is_running_microtasks_);
157 : v8::Isolate::SuppressMicrotaskExecutionScope suppress(
158 76136 : reinterpret_cast<v8::Isolate*>(isolate));
159 : HandleScopeImplementer::EnteredContextRewindScope rewind_scope(
160 : isolate->handle_scope_implementer());
161 76136 : TRACE_EVENT_BEGIN0("v8.execute", "RunMicrotasks");
162 76136 : TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks");
163 38068 : maybe_result = Execution::TryRunMicrotasks(isolate, this, &maybe_exception);
164 : processed_microtask_count =
165 38068 : static_cast<int>(finished_microtask_count_ - base_count);
166 76136 : TRACE_EVENT_END1("v8.execute", "RunMicrotasks", "microtask_count",
167 : processed_microtask_count);
168 : }
169 :
170 : // If execution is terminating, clean up and propagate that to TryCatch scope.
171 38088 : if (maybe_result.is_null() && maybe_exception.is_null()) {
172 20 : delete[] ring_buffer_;
173 20 : ring_buffer_ = nullptr;
174 20 : capacity_ = 0;
175 20 : size_ = 0;
176 20 : start_ = 0;
177 20 : isolate->SetTerminationOnExternalTryCatch();
178 : OnCompleted(isolate);
179 20 : return -1;
180 : }
181 : DCHECK_EQ(0, size());
182 : OnCompleted(isolate);
183 :
184 38048 : return processed_microtask_count;
185 : }
186 :
187 278162 : void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
188 278162 : if (size_) {
189 : // Iterate pending Microtasks as root objects to avoid the write barrier for
190 : // all single Microtask. If this hurts the GC performance, use a FixedArray.
191 1692 : visitor->VisitRootPointers(
192 : Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_ + start_),
193 2256 : FullObjectSlot(ring_buffer_ + std::min(start_ + size_, capacity_)));
194 1128 : visitor->VisitRootPointers(
195 : Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_),
196 1692 : FullObjectSlot(ring_buffer_ + std::max(start_ + size_ - capacity_,
197 2820 : static_cast<intptr_t>(0))));
198 : }
199 :
200 278162 : if (capacity_ <= kMinimumCapacity) {
201 278030 : return;
202 : }
203 :
204 132 : intptr_t new_capacity = capacity_;
205 954 : while (new_capacity > 2 * size_) {
206 411 : new_capacity >>= 1;
207 : }
208 132 : new_capacity = std::max(new_capacity, kMinimumCapacity);
209 132 : if (new_capacity < capacity_) {
210 54 : ResizeBuffer(new_capacity);
211 : }
212 : }
213 :
214 0 : int MicrotaskQueue::GetMicrotasksScopeDepth() const {
215 120937 : return microtasks_depth_;
216 : }
217 :
218 45 : void MicrotaskQueue::AddMicrotasksCompletedCallback(
219 : MicrotasksCompletedCallbackWithData callback, void* data) {
220 : CallbackWithData callback_with_data(callback, data);
221 : auto pos =
222 : std::find(microtasks_completed_callbacks_.begin(),
223 45 : microtasks_completed_callbacks_.end(), callback_with_data);
224 45 : if (pos != microtasks_completed_callbacks_.end()) return;
225 45 : microtasks_completed_callbacks_.push_back(callback_with_data);
226 : }
227 :
228 3730 : void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
229 : MicrotasksCompletedCallbackWithData callback, void* data) {
230 : CallbackWithData callback_with_data(callback, data);
231 : auto pos =
232 : std::find(microtasks_completed_callbacks_.begin(),
233 3730 : microtasks_completed_callbacks_.end(), callback_with_data);
234 7415 : if (pos == microtasks_completed_callbacks_.end()) return;
235 : microtasks_completed_callbacks_.erase(pos);
236 : }
237 :
238 121389 : void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const {
239 121389 : std::vector<CallbackWithData> callbacks(microtasks_completed_callbacks_);
240 121437 : for (auto& callback : callbacks) {
241 50 : callback.first(reinterpret_cast<v8::Isolate*>(isolate), callback.second);
242 : }
243 121387 : }
244 :
245 4 : Microtask MicrotaskQueue::get(intptr_t index) const {
246 : DCHECK_LT(index, size_);
247 4 : Object microtask(ring_buffer_[(index + start_) % capacity_]);
248 4 : return Microtask::cast(microtask);
249 : }
250 :
251 0 : void MicrotaskQueue::OnCompleted(Isolate* isolate) {
252 : // TODO(marja): (spec) The discussion about when to clear the KeepDuringJob
253 : // set is still open (whether to clear it after every microtask or once
254 : // during a microtask checkpoint). See also
255 : // https://github.com/tc39/proposal-weakrefs/issues/39 .
256 121391 : isolate->heap()->ClearKeepDuringJobSet();
257 :
258 121389 : FireMicrotasksCompletedCallback(isolate);
259 0 : }
260 :
261 3239 : void MicrotaskQueue::ResizeBuffer(intptr_t new_capacity) {
262 : DCHECK_LE(size_, new_capacity);
263 3239 : Address* new_ring_buffer = new Address[new_capacity];
264 1072613 : for (intptr_t i = 0; i < size_; ++i) {
265 534687 : new_ring_buffer[i] = ring_buffer_[(start_ + i) % capacity_];
266 : }
267 :
268 3239 : delete[] ring_buffer_;
269 3239 : ring_buffer_ = new_ring_buffer;
270 3239 : capacity_ = new_capacity;
271 3239 : start_ = 0;
272 3239 : }
273 :
274 : } // namespace internal
275 122036 : } // namespace v8
|