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
17namespace v8 {
18namespace internal {
19
20class Isolate;
21class Microtask;
22class Object;
23class RootVisitor;
24
25class 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;
44  void PerformCheckpoint(v8::Isolate* isolate) override {
45    if (!ShouldPerfomCheckpoint()) return;
46    PerformCheckpointInternal(isolate);
47  }
48
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;
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.
72  void IncrementMicrotasksScopeDepth() { ++microtasks_depth_; }
73  void DecrementMicrotasksScopeDepth() { --microtasks_depth_; }
74  int GetMicrotasksScopeDepth() const override { return microtasks_depth_; }
75
76  // Possibly nested microtasks suppression scopes prevent microtasks
77  // from running.
78  void IncrementMicrotasksSuppressions() { ++microtasks_suppressions_; }
79  void DecrementMicrotasksSuppressions() { --microtasks_suppressions_; }
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.
87  void IncrementDebugMicrotasksScopeDepth() { ++debug_microtasks_depth_; }
88  void DecrementDebugMicrotasksScopeDepth() { --debug_microtasks_depth_; }
89  bool DebugMicrotasksScopeDepthIsZero() const {
90    return debug_microtasks_depth_ == 0;
91  }
92#endif
93
94  void set_microtasks_policy(v8::MicrotasksPolicy microtasks_policy) {
95    microtasks_policy_ = microtasks_policy;
96  }
97  v8::MicrotasksPolicy microtasks_policy() const { return microtasks_policy_; }
98
99  intptr_t capacity() const { return capacity_; }
100  intptr_t size() const { return size_; }
101  intptr_t start() const { return start_; }
102
103  Microtask get(intptr_t index) const;
104
105  MicrotaskQueue* next() const { return next_; }
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