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