// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_EXECUTION_MICROTASK_QUEUE_H_ #define V8_EXECUTION_MICROTASK_QUEUE_H_ #include #include #include #include "include/v8-internal.h" // For Address. #include "include/v8-microtask-queue.h" #include "src/base/macros.h" namespace v8 { namespace internal { class Isolate; class Microtask; class Object; class RootVisitor; class V8_EXPORT_PRIVATE MicrotaskQueue final : public v8::MicrotaskQueue { public: static void SetUpDefaultMicrotaskQueue(Isolate* isolate); static std::unique_ptr New(Isolate* isolate); ~MicrotaskQueue() override; // Uses raw Address values because it's called via ExternalReference. // {raw_microtask} is a tagged Microtask pointer. // Returns Smi::kZero due to CallCFunction. static Address CallEnqueueMicrotask(Isolate* isolate, intptr_t microtask_queue_pointer, Address raw_microtask); // v8::MicrotaskQueue implementations. void EnqueueMicrotask(v8::Isolate* isolate, v8::Local microtask) override; void EnqueueMicrotask(v8::Isolate* isolate, v8::MicrotaskCallback callback, void* data) override; void PerformCheckpoint(v8::Isolate* isolate) override { if (!ShouldPerfomCheckpoint()) return; PerformCheckpointInternal(isolate); } bool ShouldPerfomCheckpoint() const { return !IsRunningMicrotasks() && !GetMicrotasksScopeDepth() && !HasMicrotasksSuppressions(); } void EnqueueMicrotask(Microtask microtask); void AddMicrotasksCompletedCallback( MicrotasksCompletedCallbackWithData callback, void* data) override; void RemoveMicrotasksCompletedCallback( MicrotasksCompletedCallbackWithData callback, void* data) override; bool IsRunningMicrotasks() const override { return is_running_microtasks_; } // Runs all queued Microtasks. // Returns -1 if the execution is terminating, otherwise, returns the number // of microtasks that ran in this round. int RunMicrotasks(Isolate* isolate); // Iterate all pending Microtasks in this queue as strong roots, so that // builtins can update the queue directly without the write barrier. void IterateMicrotasks(RootVisitor* visitor); // Microtasks scope depth represents nested scopes controlling microtasks // invocation, which happens when depth reaches zero. void IncrementMicrotasksScopeDepth() { ++microtasks_depth_; } void DecrementMicrotasksScopeDepth() { --microtasks_depth_; } int GetMicrotasksScopeDepth() const override { return microtasks_depth_; } // Possibly nested microtasks suppression scopes prevent microtasks // from running. void IncrementMicrotasksSuppressions() { ++microtasks_suppressions_; } void DecrementMicrotasksSuppressions() { --microtasks_suppressions_; } bool HasMicrotasksSuppressions() const { return microtasks_suppressions_ != 0; } #ifdef DEBUG // In debug we check that calls not intended to invoke microtasks are // still correctly wrapped with microtask scopes. void IncrementDebugMicrotasksScopeDepth() { ++debug_microtasks_depth_; } void DecrementDebugMicrotasksScopeDepth() { --debug_microtasks_depth_; } bool DebugMicrotasksScopeDepthIsZero() const { return debug_microtasks_depth_ == 0; } #endif void set_microtasks_policy(v8::MicrotasksPolicy microtasks_policy) { microtasks_policy_ = microtasks_policy; } v8::MicrotasksPolicy microtasks_policy() const { return microtasks_policy_; } intptr_t capacity() const { return capacity_; } intptr_t size() const { return size_; } intptr_t start() const { return start_; } Microtask get(intptr_t index) const; MicrotaskQueue* next() const { return next_; } MicrotaskQueue* prev() const { return prev_; } static const size_t kRingBufferOffset; static const size_t kCapacityOffset; static const size_t kSizeOffset; static const size_t kStartOffset; static const size_t kFinishedMicrotaskCountOffset; static const intptr_t kMinimumCapacity; private: void PerformCheckpointInternal(v8::Isolate* v8_isolate); void OnCompleted(Isolate* isolate) const; MicrotaskQueue(); void ResizeBuffer(intptr_t new_capacity); // A ring buffer to hold Microtask instances. // ring_buffer_[(start_ + i) % capacity_] contains |i|th Microtask for each // |i| in [0, size_). intptr_t size_ = 0; intptr_t capacity_ = 0; intptr_t start_ = 0; Address* ring_buffer_ = nullptr; // The number of finished microtask. intptr_t finished_microtask_count_ = 0; // MicrotaskQueue instances form a doubly linked list loop, so that all // instances are reachable through |next_|. MicrotaskQueue* next_ = nullptr; MicrotaskQueue* prev_ = nullptr; int microtasks_depth_ = 0; int microtasks_suppressions_ = 0; #ifdef DEBUG int debug_microtasks_depth_ = 0; #endif v8::MicrotasksPolicy microtasks_policy_ = v8::MicrotasksPolicy::kAuto; bool is_running_microtasks_ = false; using CallbackWithData = std::pair; std::vector microtasks_completed_callbacks_; }; } // namespace internal } // namespace v8 #endif // V8_EXECUTION_MICROTASK_QUEUE_H_