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