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