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.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 61048 : void MicrotaskQueue::SetUpDefaultMicrotaskQueue(Isolate* isolate) {
35 : DCHECK_NULL(isolate->default_microtask_queue());
36 :
37 61048 : MicrotaskQueue* microtask_queue = new MicrotaskQueue;
38 61049 : microtask_queue->next_ = microtask_queue;
39 61049 : microtask_queue->prev_ = microtask_queue;
40 : isolate->set_default_microtask_queue(microtask_queue);
41 61049 : }
42 :
43 : // static
44 12 : std::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate) {
45 : DCHECK_NOT_NULL(isolate->default_microtask_queue());
46 :
47 6 : std::unique_ptr<MicrotaskQueue> microtask_queue(new MicrotaskQueue);
48 :
49 : // Insert the new instance to the next of last MicrotaskQueue instance.
50 6 : MicrotaskQueue* last = isolate->default_microtask_queue()->prev_;
51 6 : microtask_queue->next_ = last->next_;
52 6 : microtask_queue->prev_ = last;
53 12 : last->next_->prev_ = microtask_queue.get();
54 6 : last->next_ = microtask_queue.get();
55 :
56 6 : return microtask_queue;
57 : }
58 :
59 : MicrotaskQueue::MicrotaskQueue() = default;
60 :
61 61040 : MicrotaskQueue::~MicrotaskQueue() {
62 61040 : if (next_ != this) {
63 : DCHECK_NE(prev_, this);
64 6 : next_->prev_ = prev_;
65 6 : prev_->next_ = next_;
66 : }
67 61040 : delete[] ring_buffer_;
68 61040 : }
69 :
70 : // static
71 2654 : Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
72 : intptr_t microtask_queue_pointer,
73 : Address raw_microtask) {
74 2654 : Microtask microtask = Microtask::cast(Object(raw_microtask));
75 : reinterpret_cast<MicrotaskQueue*>(microtask_queue_pointer)
76 2654 : ->EnqueueMicrotask(microtask);
77 2654 : return ReadOnlyRoots(isolate).undefined_value().ptr();
78 : }
79 :
80 14090 : void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
81 14090 : if (size_ == capacity_) {
82 : // Keep the capacity of |ring_buffer_| power of 2, so that the JIT
83 : // implementation can calculate the modulo easily.
84 6198 : intptr_t new_capacity = std::max(kMinimumCapacity, capacity_ << 1);
85 3099 : ResizeBuffer(new_capacity);
86 : }
87 :
88 : DCHECK_LT(size_, capacity_);
89 28180 : ring_buffer_[(start_ + size_) % capacity_] = microtask.ptr();
90 14090 : ++size_;
91 14090 : }
92 :
93 : namespace {
94 :
95 : class SetIsRunningMicrotasks {
96 : public:
97 : explicit SetIsRunningMicrotasks(bool* flag) : flag_(flag) {
98 : DCHECK(!*flag_);
99 51534 : *flag_ = true;
100 : }
101 :
102 : ~SetIsRunningMicrotasks() {
103 : DCHECK(*flag_);
104 51534 : *flag_ = false;
105 : }
106 :
107 : private:
108 : bool* flag_;
109 : };
110 :
111 : } // namespace
112 :
113 193279 : int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
114 141745 : if (!size()) {
115 : OnCompleted(isolate);
116 90209 : return 0;
117 : }
118 :
119 51534 : intptr_t base_count = finished_microtask_count_;
120 :
121 : HandleScope handle_scope(isolate);
122 51534 : MaybeHandle<Object> maybe_exception;
123 :
124 : MaybeHandle<Object> maybe_result;
125 :
126 : int processed_microtask_count;
127 : {
128 51534 : SetIsRunningMicrotasks scope(&is_running_microtasks_);
129 : v8::Isolate::SuppressMicrotaskExecutionScope suppress(
130 103068 : reinterpret_cast<v8::Isolate*>(isolate));
131 : HandleScopeImplementer::EnteredContextRewindScope rewind_scope(
132 : isolate->handle_scope_implementer());
133 103068 : TRACE_EVENT_BEGIN0("v8.execute", "RunMicrotasks");
134 154602 : TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks");
135 51534 : maybe_result = Execution::TryRunMicrotasks(isolate, this, &maybe_exception);
136 : processed_microtask_count =
137 51534 : static_cast<int>(finished_microtask_count_ - base_count);
138 103068 : TRACE_EVENT_END1("v8.execute", "RunMicrotasks", "microtask_count",
139 : processed_microtask_count);
140 : }
141 :
142 : // If execution is terminating, clean up and propagate that to TryCatch scope.
143 51554 : if (maybe_result.is_null() && maybe_exception.is_null()) {
144 20 : delete[] ring_buffer_;
145 20 : ring_buffer_ = nullptr;
146 20 : capacity_ = 0;
147 20 : size_ = 0;
148 20 : start_ = 0;
149 20 : isolate->SetTerminationOnExternalTryCatch();
150 : OnCompleted(isolate);
151 20 : return -1;
152 : }
153 : DCHECK_EQ(0, size());
154 : OnCompleted(isolate);
155 :
156 51514 : return processed_microtask_count;
157 : }
158 :
159 280838 : void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
160 280838 : if (size_) {
161 : // Iterate pending Microtasks as root objects to avoid the write barrier for
162 : // all single Microtask. If this hurts the GC performance, use a FixedArray.
163 : visitor->VisitRootPointers(
164 : Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_ + start_),
165 2392 : FullObjectSlot(ring_buffer_ + std::min(start_ + size_, capacity_)));
166 : visitor->VisitRootPointers(
167 : Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_),
168 598 : FullObjectSlot(ring_buffer_ + std::max(start_ + size_ - capacity_,
169 2392 : static_cast<intptr_t>(0))));
170 : }
171 :
172 280838 : if (capacity_ <= kMinimumCapacity) {
173 280703 : return;
174 : }
175 :
176 135 : intptr_t new_capacity = capacity_;
177 676 : while (new_capacity > 2 * size_) {
178 406 : new_capacity >>= 1;
179 : }
180 135 : new_capacity = std::max(new_capacity, kMinimumCapacity);
181 135 : if (new_capacity < capacity_) {
182 50 : ResizeBuffer(new_capacity);
183 : }
184 : }
185 :
186 45 : void MicrotaskQueue::AddMicrotasksCompletedCallback(
187 : MicrotasksCompletedCallback callback) {
188 : auto pos = std::find(microtasks_completed_callbacks_.begin(),
189 : microtasks_completed_callbacks_.end(), callback);
190 90 : if (pos != microtasks_completed_callbacks_.end()) return;
191 45 : microtasks_completed_callbacks_.push_back(callback);
192 : }
193 :
194 3683 : void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
195 : MicrotasksCompletedCallback callback) {
196 : auto pos = std::find(microtasks_completed_callbacks_.begin(),
197 : microtasks_completed_callbacks_.end(), callback);
198 7366 : if (pos == microtasks_completed_callbacks_.end()) return;
199 45 : microtasks_completed_callbacks_.erase(pos);
200 : }
201 :
202 141742 : void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const {
203 : std::vector<MicrotasksCompletedCallback> callbacks(
204 141742 : microtasks_completed_callbacks_);
205 283534 : for (auto& callback : callbacks) {
206 50 : callback(reinterpret_cast<v8::Isolate*>(isolate));
207 : }
208 141742 : }
209 :
210 0 : void MicrotaskQueue::OnCompleted(Isolate* isolate) {
211 : // TODO(marja): (spec) The discussion about when to clear the KeepDuringJob
212 : // set is still open (whether to clear it after every microtask or once
213 : // during a microtask checkpoint). See also
214 : // https://github.com/tc39/proposal-weakrefs/issues/39 .
215 141745 : isolate->heap()->ClearKeepDuringJobSet();
216 :
217 141744 : FireMicrotasksCompletedCallback(isolate);
218 0 : }
219 :
220 3149 : void MicrotaskQueue::ResizeBuffer(intptr_t new_capacity) {
221 : DCHECK_LE(size_, new_capacity);
222 3149 : Address* new_ring_buffer = new Address[new_capacity];
223 599119 : for (intptr_t i = 0; i < size_; ++i) {
224 595970 : new_ring_buffer[i] = ring_buffer_[(start_ + i) % capacity_];
225 : }
226 :
227 3149 : delete[] ring_buffer_;
228 3149 : ring_buffer_ = new_ring_buffer;
229 3149 : capacity_ = new_capacity;
230 3149 : start_ = 0;
231 3149 : }
232 :
233 : } // namespace internal
234 178779 : } // namespace v8
|