summaryrefslogtreecommitdiff
path: root/deps/v8/src/microtask-queue.h
blob: ce0908852645f8d40a8ba13439d182c529044a77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// 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_MICROTASK_QUEUE_H_
#define V8_MICROTASK_QUEUE_H_

#include <stdint.h>
#include <memory>
#include <vector>

#include "include/v8-internal.h"  // For Address.
#include "include/v8.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<MicrotaskQueue> New(Isolate* isolate);

  ~MicrotaskQueue();

  // Uses raw Address values because it's called via ExternalReference.
  // {raw_microtask} is a tagged Microtask pointer.
  // Returns a tagged Object pointer.
  static Address CallEnqueueMicrotask(Isolate* isolate,
                                      intptr_t microtask_queue_pointer,
                                      Address raw_microtask);

  // v8::MicrotaskQueue implementations.
  void EnqueueMicrotask(v8::Isolate* isolate,
                        v8::Local<Function> microtask) override;
  void EnqueueMicrotask(v8::Isolate* isolate, v8::MicrotaskCallback callback,
                        void* data) override;
  void PerformCheckpoint(v8::Isolate* isolate) override;

  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 { 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_; }

  void FireMicrotasksCompletedCallback(Isolate* isolate) const;

  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 OnCompleted(Isolate* isolate);

  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<MicrotasksCompletedCallbackWithData, void*>;
  std::vector<CallbackWithData> microtasks_completed_callbacks_;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_MICROTASK_QUEUE_H_