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#ifndef V8_MICROTASK_QUEUE_H_
6#define V8_MICROTASK_QUEUE_H_
7
8#include <stdint.h>
9#include <memory>
10#include <vector>
11
12#include "include/v8-internal.h" // For Address.
13#include "include/v8.h"
14#include "src/base/macros.h"
15
16namespace v8 {
17namespace internal {
18
19class Isolate;
20class Microtask;
21class Object;
22class RootVisitor;
23
24class V8_EXPORT_PRIVATE MicrotaskQueue final : public v8::MicrotaskQueue {
25 public:
26 static void SetUpDefaultMicrotaskQueue(Isolate* isolate);
27 static std::unique_ptr<MicrotaskQueue> New(Isolate* isolate);
28
29 ~MicrotaskQueue();
30
31 // Uses raw Address values because it's called via ExternalReference.
32 // {raw_microtask} is a tagged Microtask pointer.
33 // Returns a tagged Object pointer.
34 static Address CallEnqueueMicrotask(Isolate* isolate,
35 intptr_t microtask_queue_pointer,
36 Address raw_microtask);
37
38 // v8::MicrotaskQueue implementations.
39 void EnqueueMicrotask(v8::Isolate* isolate,
40 v8::Local<Function> microtask) override;
41 void EnqueueMicrotask(v8::Isolate* isolate, v8::MicrotaskCallback callback,
42 void* data) override;
43 void PerformCheckpoint(v8::Isolate* isolate) override;
44
45 void EnqueueMicrotask(Microtask microtask);
46 void AddMicrotasksCompletedCallback(
47 MicrotasksCompletedCallbackWithData callback, void* data) override;
48 void RemoveMicrotasksCompletedCallback(
49 MicrotasksCompletedCallbackWithData callback, void* data) override;
50 bool IsRunningMicrotasks() const override { return is_running_microtasks_; }
51
52 // Runs all queued Microtasks.
53 // Returns -1 if the execution is terminating, otherwise, returns the number
54 // of microtasks that ran in this round.
55 int RunMicrotasks(Isolate* isolate);
56
57 // Iterate all pending Microtasks in this queue as strong roots, so that
58 // builtins can update the queue directly without the write barrier.
59 void IterateMicrotasks(RootVisitor* visitor);
60
61 // Microtasks scope depth represents nested scopes controlling microtasks
62 // invocation, which happens when depth reaches zero.
63 void IncrementMicrotasksScopeDepth() { ++microtasks_depth_; }
64 void DecrementMicrotasksScopeDepth() { --microtasks_depth_; }
65 int GetMicrotasksScopeDepth() const { return microtasks_depth_; }
66
67 // Possibly nested microtasks suppression scopes prevent microtasks
68 // from running.
69 void IncrementMicrotasksSuppressions() { ++microtasks_suppressions_; }
70 void DecrementMicrotasksSuppressions() { --microtasks_suppressions_; }
71 bool HasMicrotasksSuppressions() const {
72 return microtasks_suppressions_ != 0;
73 }
74
75#ifdef DEBUG
76 // In debug we check that calls not intended to invoke microtasks are
77 // still correctly wrapped with microtask scopes.
78 void IncrementDebugMicrotasksScopeDepth() { ++debug_microtasks_depth_; }
79 void DecrementDebugMicrotasksScopeDepth() { --debug_microtasks_depth_; }
80 bool DebugMicrotasksScopeDepthIsZero() const {
81 return debug_microtasks_depth_ == 0;
82 }
83#endif
84
85 void set_microtasks_policy(v8::MicrotasksPolicy microtasks_policy) {
86 microtasks_policy_ = microtasks_policy;
87 }
88 v8::MicrotasksPolicy microtasks_policy() const { return microtasks_policy_; }
89
90 void FireMicrotasksCompletedCallback(Isolate* isolate) const;
91
92 intptr_t capacity() const { return capacity_; }
93 intptr_t size() const { return size_; }
94 intptr_t start() const { return start_; }
95
96 Microtask get(intptr_t index) const;
97
98 MicrotaskQueue* next() const { return next_; }
99 MicrotaskQueue* prev() const { return prev_; }
100
101 static const size_t kRingBufferOffset;
102 static const size_t kCapacityOffset;
103 static const size_t kSizeOffset;
104 static const size_t kStartOffset;
105 static const size_t kFinishedMicrotaskCountOffset;
106
107 static const intptr_t kMinimumCapacity;
108
109 private:
110 void OnCompleted(Isolate* isolate);
111
112 MicrotaskQueue();
113 void ResizeBuffer(intptr_t new_capacity);
114
115 // A ring buffer to hold Microtask instances.
116 // ring_buffer_[(start_ + i) % capacity_] contains |i|th Microtask for each
117 // |i| in [0, size_).
118 intptr_t size_ = 0;
119 intptr_t capacity_ = 0;
120 intptr_t start_ = 0;
121 Address* ring_buffer_ = nullptr;
122
123 // The number of finished microtask.
124 intptr_t finished_microtask_count_ = 0;
125
126 // MicrotaskQueue instances form a doubly linked list loop, so that all
127 // instances are reachable through |next_|.
128 MicrotaskQueue* next_ = nullptr;
129 MicrotaskQueue* prev_ = nullptr;
130
131 int microtasks_depth_ = 0;
132 int microtasks_suppressions_ = 0;
133#ifdef DEBUG
134 int debug_microtasks_depth_ = 0;
135#endif
136
137 v8::MicrotasksPolicy microtasks_policy_ = v8::MicrotasksPolicy::kAuto;
138
139 bool is_running_microtasks_ = false;
140 using CallbackWithData =
141 std::pair<MicrotasksCompletedCallbackWithData, void*>;
142 std::vector<CallbackWithData> microtasks_completed_callbacks_;
143};
144
145} // namespace internal
146} // namespace v8
147
148#endif // V8_MICROTASK_QUEUE_H_
149