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 #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 
16 namespace v8 {
17 namespace internal {
18 
19 class Isolate;
20 class Microtask;
21 class Object;
22 class RootVisitor;
23 
24 class 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() override;
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;
IsRunningMicrotasks()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.
IncrementMicrotasksScopeDepth()63   void IncrementMicrotasksScopeDepth() { ++microtasks_depth_; }
DecrementMicrotasksScopeDepth()64   void DecrementMicrotasksScopeDepth() { --microtasks_depth_; }
65   int GetMicrotasksScopeDepth() const override;
66 
67   // Possibly nested microtasks suppression scopes prevent microtasks
68   // from running.
IncrementMicrotasksSuppressions()69   void IncrementMicrotasksSuppressions() { ++microtasks_suppressions_; }
DecrementMicrotasksSuppressions()70   void DecrementMicrotasksSuppressions() { --microtasks_suppressions_; }
HasMicrotasksSuppressions()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.
IncrementDebugMicrotasksScopeDepth()78   void IncrementDebugMicrotasksScopeDepth() { ++debug_microtasks_depth_; }
DecrementDebugMicrotasksScopeDepth()79   void DecrementDebugMicrotasksScopeDepth() { --debug_microtasks_depth_; }
DebugMicrotasksScopeDepthIsZero()80   bool DebugMicrotasksScopeDepthIsZero() const {
81     return debug_microtasks_depth_ == 0;
82   }
83 #endif
84 
set_microtasks_policy(v8::MicrotasksPolicy microtasks_policy)85   void set_microtasks_policy(v8::MicrotasksPolicy microtasks_policy) {
86     microtasks_policy_ = microtasks_policy;
87   }
microtasks_policy()88   v8::MicrotasksPolicy microtasks_policy() const { return microtasks_policy_; }
89 
90   void FireMicrotasksCompletedCallback(Isolate* isolate) const;
91 
capacity()92   intptr_t capacity() const { return capacity_; }
size()93   intptr_t size() const { return size_; }
start()94   intptr_t start() const { return start_; }
95 
96   Microtask get(intptr_t index) const;
97 
next()98   MicrotaskQueue* next() const { return next_; }
prev()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_EXECUTION_MICROTASK_QUEUE_H_
149