v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
microtask-queue.cc
Go to the documentation of this file.
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
6
7#include <algorithm>
8#include <cstddef>
9#include <optional>
10
11#include "src/api/api-inl.h"
12#include "src/base/logging.h"
17#include "src/roots/roots-inl.h"
19
20namespace v8 {
21namespace internal {
22
24 OFFSET_OF(MicrotaskQueue, ring_buffer_);
26 OFFSET_OF(MicrotaskQueue, capacity_);
27const size_t MicrotaskQueue::kSizeOffset = OFFSET_OF(MicrotaskQueue, size_);
28const size_t MicrotaskQueue::kStartOffset = OFFSET_OF(MicrotaskQueue, start_);
30 OFFSET_OF(MicrotaskQueue, finished_microtask_count_);
31
32const intptr_t MicrotaskQueue::kMinimumCapacity = 8;
33
34// static
36 DCHECK_NULL(isolate->default_microtask_queue());
37
41 isolate->set_default_microtask_queue(microtask_queue);
42}
43
44// static
45std::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate) {
46 DCHECK_NOT_NULL(isolate->default_microtask_queue());
47
48 std::unique_ptr<MicrotaskQueue> microtask_queue(new MicrotaskQueue);
49
50 // Insert the new instance to the next of last MicrotaskQueue instance.
51 MicrotaskQueue* last = isolate->default_microtask_queue()->prev_;
53 microtask_queue->prev_ = last;
54 last->next_->prev_ = microtask_queue.get();
55 last->next_ = microtask_queue.get();
56
57 return microtask_queue;
58}
59
61
63 if (next_ != this) {
64 DCHECK_NE(prev_, this);
65 next_->prev_ = prev_;
66 prev_->next_ = next_;
67 }
68 delete[] ring_buffer_;
69}
70
71// static
73 intptr_t microtask_queue_pointer,
74 Address raw_microtask) {
75 Tagged<Microtask> microtask = Cast<Microtask>(Tagged<Object>(raw_microtask));
76 reinterpret_cast<MicrotaskQueue*>(microtask_queue_pointer)
77 ->EnqueueMicrotask(microtask);
78 return Smi::zero().ptr();
79}
80
82 v8::Local<Function> function) {
83 Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
84 HandleScope scope(isolate);
85 DirectHandle<CallableTask> microtask = isolate->factory()->NewCallableTask(
86 Utils::OpenDirectHandle(*function), isolate->native_context());
87 EnqueueMicrotask(*microtask);
88}
89
92 void* data) {
93 Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
94 HandleScope scope(isolate);
95 DirectHandle<CallbackTask> microtask = isolate->factory()->NewCallbackTask(
96 isolate->factory()->NewForeign<kMicrotaskCallbackTag>(
97 reinterpret_cast<Address>(callback)),
98 isolate->factory()->NewForeign<kMicrotaskCallbackDataTag>(
99 reinterpret_cast<Address>(data)));
100 EnqueueMicrotask(*microtask);
101}
102
104 if (size_ == capacity_) {
105 // Keep the capacity of |ring_buffer_| power of 2, so that the JIT
106 // implementation can calculate the modulo easily.
107 intptr_t new_capacity = std::max(kMinimumCapacity, capacity_ << 1);
108 ResizeBuffer(new_capacity);
109 }
110
112 ring_buffer_[(start_ + size_) % capacity_] = microtask.ptr();
113 ++size_;
114}
115
118 std::optional<MicrotasksScope> microtasks_scope;
120 // If we're using microtask scopes to schedule microtask execution, V8
121 // API calls will check that there's always a microtask scope on the
122 // stack. As the microtasks we're about to execute could invoke embedder
123 // callbacks which then calls back into V8, we create an artificial
124 // microtask scope here to avoid running into the CallDepthScope check.
125 microtasks_scope.emplace(v8_isolate, this,
127 }
128 Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
129 RunMicrotasks(isolate);
130 isolate->ClearKeptObjects();
131}
132
133namespace {
134
135class SetIsRunningMicrotasks {
136 public:
137 explicit SetIsRunningMicrotasks(bool* flag) : flag_(flag) {
138 DCHECK(!*flag_);
139 *flag_ = true;
140 }
141
142 ~SetIsRunningMicrotasks() {
143 DCHECK(*flag_);
144 *flag_ = false;
145 }
146
147 private:
148 bool* flag_;
149};
150
151} // namespace
152
154 SetIsRunningMicrotasks scope(&is_running_microtasks_);
156 reinterpret_cast<v8::Isolate*>(isolate), this);
157
158 if (!size()) {
159 OnCompleted(isolate);
160 return 0;
161 }
162
163 // We should not enter V8 if it's marked for termination.
164 DCHECK_IMPLIES(v8_flags.strict_termination_checks,
165 !isolate->is_execution_terminating());
166
167 intptr_t base_count = finished_microtask_count_;
168 HandleScope handle_scope(isolate);
169 MaybeDirectHandle<Object> maybe_result;
170
171#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
172 DirectHandle<Object> continuation_preserved_embedder_data(
173 isolate->isolate_data()->continuation_preserved_embedder_data(), isolate);
174 isolate->isolate_data()->set_continuation_preserved_embedder_data(
175 ReadOnlyRoots(isolate).undefined_value());
176#endif // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
177
178 int processed_microtask_count;
179 {
181 isolate->handle_scope_implementer());
182 TRACE_EVENT_BEGIN0("v8.execute", "RunMicrotasks");
183 {
184 TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks");
185 maybe_result = Execution::TryRunMicrotasks(isolate, this);
186 processed_microtask_count =
187 static_cast<int>(finished_microtask_count_ - base_count);
188 }
189 TRACE_EVENT_END1("v8.execute", "RunMicrotasks", "microtask_count",
190 processed_microtask_count);
191 }
192
193#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
194 isolate->isolate_data()->set_continuation_preserved_embedder_data(
195 *continuation_preserved_embedder_data);
196#endif // V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
197
198 if (isolate->is_execution_terminating()) {
199 DCHECK(isolate->has_exception());
200 DCHECK(maybe_result.is_null());
201 delete[] ring_buffer_;
202 ring_buffer_ = nullptr;
203 capacity_ = 0;
204 size_ = 0;
205 start_ = 0;
206 isolate->OnTerminationDuringRunMicrotasks();
207 OnCompleted(isolate);
208 return -1;
209 }
210
211 DCHECK_EQ(0, size());
212 OnCompleted(isolate);
213
214 return processed_microtask_count;
215}
216
218 if (size_) {
219 // Iterate pending Microtasks as root objects to avoid the write barrier for
220 // all single Microtask. If this hurts the GC performance, use a FixedArray.
221 visitor->VisitRootPointers(
222 Root::kMicroTasks, nullptr, FullObjectSlot(ring_buffer_ + start_),
224 visitor->VisitRootPointers(
225 Root::kMicroTasks, nullptr, FullObjectSlot(ring_buffer_),
227 static_cast<intptr_t>(0))));
228 }
229
231 return;
232 }
233
234 intptr_t new_capacity = capacity_;
235 while (new_capacity > 2 * size_) {
236 new_capacity >>= 1;
237 }
238 new_capacity = std::max(new_capacity, kMinimumCapacity);
239 if (new_capacity < capacity_) {
240 ResizeBuffer(new_capacity);
241 }
242}
243
246 std::vector<CallbackWithData>* microtasks_completed_callbacks =
249 if (!microtasks_completed_callbacks_cow_.has_value()) {
252 }
253 // Use the COW vector if we are iterating the callbacks right now.
254 microtasks_completed_callbacks =
256 }
257
258 CallbackWithData callback_with_data(callback, data);
259 const auto pos =
260 std::find(microtasks_completed_callbacks->begin(),
261 microtasks_completed_callbacks->end(), callback_with_data);
262 if (pos != microtasks_completed_callbacks->end()) {
263 return;
264 }
265 microtasks_completed_callbacks->push_back(callback_with_data);
266}
267
270 std::vector<CallbackWithData>* microtasks_completed_callbacks =
273 if (!microtasks_completed_callbacks_cow_.has_value()) {
276 }
277 // Use the COW vector if we are iterating the callbacks right now.
278 microtasks_completed_callbacks =
280 }
281
282 CallbackWithData callback_with_data(callback, data);
283 const auto pos =
284 std::find(microtasks_completed_callbacks->begin(),
285 microtasks_completed_callbacks->end(), callback_with_data);
286 if (pos == microtasks_completed_callbacks->end()) {
287 return;
288 }
289 microtasks_completed_callbacks->erase(pos);
290}
291
304
306 DCHECK_LT(index, size_);
307 Tagged<Object> microtask(ring_buffer_[(index + start_) % capacity_]);
308 return Cast<Microtask>(microtask);
309}
310
311void MicrotaskQueue::ResizeBuffer(intptr_t new_capacity) {
312 DCHECK_LE(size_, new_capacity);
313 Address* new_ring_buffer = new Address[new_capacity];
314 for (intptr_t i = 0; i < size_; ++i) {
315 new_ring_buffer[i] = ring_buffer_[(start_ + i) % capacity_];
316 }
317
318 delete[] ring_buffer_;
319 ring_buffer_ = new_ring_buffer;
320 capacity_ = new_capacity;
321 start_ = 0;
322}
323
324} // namespace internal
325} // namespace v8
SourcePosition pos
static v8::internal::DirectHandle< To > OpenDirectHandle(v8::Local< From > handle)
Definition api.h:279
static MaybeDirectHandle< Object > TryRunMicrotasks(Isolate *isolate, MicrotaskQueue *microtask_queue)
Definition execution.cc:602
V8_INLINE bool is_null() const
v8::MicrotasksPolicy microtasks_policy_
static Address CallEnqueueMicrotask(Isolate *isolate, intptr_t microtask_queue_pointer, Address raw_microtask)
void OnCompleted(Isolate *isolate)
void EnqueueMicrotask(v8::Isolate *isolate, v8::Local< Function > microtask) override
void PerformCheckpointInternal(v8::Isolate *v8_isolate)
static const size_t kRingBufferOffset
static const size_t kSizeOffset
static const intptr_t kMinimumCapacity
static void SetUpDefaultMicrotaskQueue(Isolate *isolate)
void IterateMicrotasks(RootVisitor *visitor)
static const size_t kCapacityOffset
std::pair< MicrotasksCompletedCallbackWithData, void * > CallbackWithData
void RemoveMicrotasksCompletedCallback(MicrotasksCompletedCallbackWithData callback, void *data) override
static const size_t kFinishedMicrotaskCountOffset
int RunMicrotasks(Isolate *isolate)
Tagged< Microtask > get(intptr_t index) const
std::vector< CallbackWithData > microtasks_completed_callbacks_
void ResizeBuffer(intptr_t new_capacity)
void AddMicrotasksCompletedCallback(MicrotasksCompletedCallbackWithData callback, void *data) override
static const size_t kStartOffset
std::optional< std::vector< CallbackWithData > > microtasks_completed_callbacks_cow_
static std::unique_ptr< MicrotaskQueue > New(Isolate *isolate)
virtual void VisitRootPointers(Root root, const char *description, FullObjectSlot start, FullObjectSlot end)=0
static constexpr Tagged< Smi > zero()
Definition smi.h:99
V8_INLINE constexpr StorageType ptr() const
const int size_
Definition assembler.cc:132
uint8_t *const start_
Definition assembler.cc:131
MicrotaskQueue * microtask_queue
Definition execution.cc:77
TNode< Object > callback
bool * flag_
@ kMicrotaskCallbackDataTag
V8_EXPORT_PRIVATE FlagValues v8_flags
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
void(*)(void *data) MicrotaskCallback
void(*)(Isolate *, void *) MicrotasksCompletedCallbackWithData
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NULL(val)
Definition logging.h:491
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define OFFSET_OF(type, field)
Definition macros.h:57
#define TRACE_EVENT_END1(category_group, name, arg1_name, arg1_val)
#define TRACE_EVENT_BEGIN0(category_group, name)
#define TRACE_EVENT_CALL_STATS_SCOPED(isolate, category_group, name)
#define V8_UNLIKELY(condition)
Definition v8config.h:660