Line data Source code
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 : #include "src/microtask-queue.h"
6 :
7 : #include <algorithm>
8 : #include <functional>
9 : #include <memory>
10 : #include <vector>
11 :
12 : #include "src/heap/factory.h"
13 : #include "src/objects/foreign.h"
14 : #include "src/visitors.h"
15 : #include "test/unittests/test-utils.h"
16 : #include "testing/gtest/include/gtest/gtest.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 :
21 : using Closure = std::function<void()>;
22 :
23 21 : void RunStdFunction(void* data) {
24 : std::unique_ptr<Closure> f(static_cast<Closure*>(data));
25 21 : (*f)();
26 21 : }
27 :
28 12 : class MicrotaskQueueTest : public TestWithNativeContext {
29 : public:
30 : template <typename F>
31 21 : Handle<Microtask> NewMicrotask(F&& f) {
32 : Handle<Foreign> runner =
33 42 : factory()->NewForeign(reinterpret_cast<Address>(&RunStdFunction));
34 : Handle<Foreign> data = factory()->NewForeign(
35 63 : reinterpret_cast<Address>(new Closure(std::forward<F>(f))));
36 21 : return factory()->NewCallbackTask(runner, data);
37 : }
38 :
39 4 : void SetUp() override {
40 8 : microtask_queue_ = MicrotaskQueue::New(isolate());
41 : native_context()->set_microtask_queue(microtask_queue());
42 4 : }
43 :
44 4 : void TearDown() override {
45 4 : if (microtask_queue()) {
46 3 : microtask_queue()->RunMicrotasks(isolate());
47 3 : context()->DetachGlobal();
48 : }
49 4 : }
50 :
51 : MicrotaskQueue* microtask_queue() const { return microtask_queue_.get(); }
52 :
53 1 : void ClearTestMicrotaskQueue() {
54 1 : context()->DetachGlobal();
55 : microtask_queue_ = nullptr;
56 1 : }
57 :
58 : private:
59 : std::unique_ptr<MicrotaskQueue> microtask_queue_;
60 : };
61 :
62 : class RecordingVisitor : public RootVisitor {
63 : public:
64 1 : RecordingVisitor() = default;
65 1 : ~RecordingVisitor() override = default;
66 :
67 2 : void VisitRootPointers(Root root, const char* description,
68 : FullObjectSlot start, FullObjectSlot end) override {
69 9 : for (FullObjectSlot current = start; current != end; ++current) {
70 10 : visited_.push_back(*current);
71 : }
72 2 : }
73 :
74 : const std::vector<Object>& visited() const { return visited_; }
75 :
76 : private:
77 : std::vector<Object> visited_;
78 : };
79 :
80 : // Sanity check. Ensure a microtask is stored in a queue and run.
81 15128 : TEST_F(MicrotaskQueueTest, EnqueueAndRun) {
82 1 : bool ran = false;
83 2 : EXPECT_EQ(0, microtask_queue()->capacity());
84 2 : EXPECT_EQ(0, microtask_queue()->size());
85 1 : microtask_queue()->EnqueueMicrotask(*NewMicrotask([&ran] {
86 2 : EXPECT_FALSE(ran);
87 1 : ran = true;
88 3 : }));
89 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
90 2 : EXPECT_EQ(1, microtask_queue()->size());
91 1 : microtask_queue()->RunMicrotasks(isolate());
92 1 : EXPECT_TRUE(ran);
93 2 : EXPECT_EQ(0, microtask_queue()->size());
94 1 : }
95 :
96 : // Check for a buffer growth.
97 15128 : TEST_F(MicrotaskQueueTest, BufferGrowth) {
98 1 : int count = 0;
99 :
100 : // Enqueue and flush the queue first to have non-zero |start_|.
101 : microtask_queue()->EnqueueMicrotask(
102 5 : *NewMicrotask([&count] { EXPECT_EQ(0, count++); }));
103 1 : microtask_queue()->RunMicrotasks(isolate());
104 :
105 1 : EXPECT_LT(0, microtask_queue()->capacity());
106 2 : EXPECT_EQ(0, microtask_queue()->size());
107 2 : EXPECT_EQ(1, microtask_queue()->start());
108 :
109 : // Fill the queue with Microtasks.
110 9 : for (int i = 1; i <= MicrotaskQueue::kMinimumCapacity; ++i) {
111 : microtask_queue()->EnqueueMicrotask(
112 40 : *NewMicrotask([&count, i] { EXPECT_EQ(i, count++); }));
113 : }
114 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
115 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->size());
116 :
117 : // Add another to grow the ring buffer.
118 : microtask_queue()->EnqueueMicrotask(*NewMicrotask(
119 5 : [&] { EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1, count++); }));
120 :
121 1 : EXPECT_LT(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
122 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1, microtask_queue()->size());
123 :
124 : // Run all pending Microtasks to ensure they run in the proper order.
125 1 : microtask_queue()->RunMicrotasks(isolate());
126 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 2, count);
127 1 : }
128 :
129 : // MicrotaskQueue instances form a doubly linked list.
130 15128 : TEST_F(MicrotaskQueueTest, InstanceChain) {
131 1 : ClearTestMicrotaskQueue();
132 :
133 1 : MicrotaskQueue* default_mtq = isolate()->default_microtask_queue();
134 2 : ASSERT_TRUE(default_mtq);
135 7 : EXPECT_EQ(default_mtq, default_mtq->next());
136 3 : EXPECT_EQ(default_mtq, default_mtq->prev());
137 :
138 : // Create two instances, and check their connection.
139 : // The list contains all instances in the creation order, and the next of the
140 : // last instance is the first instance:
141 : // default_mtq -> mtq1 -> mtq2 -> default_mtq.
142 1 : std::unique_ptr<MicrotaskQueue> mtq1 = MicrotaskQueue::New(isolate());
143 1 : std::unique_ptr<MicrotaskQueue> mtq2 = MicrotaskQueue::New(isolate());
144 3 : EXPECT_EQ(default_mtq->next(), mtq1.get());
145 3 : EXPECT_EQ(mtq1->next(), mtq2.get());
146 2 : EXPECT_EQ(mtq2->next(), default_mtq);
147 2 : EXPECT_EQ(default_mtq, mtq1->prev());
148 3 : EXPECT_EQ(mtq1.get(), mtq2->prev());
149 4 : EXPECT_EQ(mtq2.get(), default_mtq->prev());
150 :
151 : // Deleted item should be also removed from the list.
152 : mtq1 = nullptr;
153 3 : EXPECT_EQ(default_mtq->next(), mtq2.get());
154 2 : EXPECT_EQ(mtq2->next(), default_mtq);
155 2 : EXPECT_EQ(default_mtq, mtq2->prev());
156 4 : EXPECT_EQ(mtq2.get(), default_mtq->prev());
157 : }
158 :
159 : // Pending Microtasks in MicrotaskQueues are strong roots. Ensure they are
160 : // visited exactly once.
161 15128 : TEST_F(MicrotaskQueueTest, VisitRoot) {
162 : // Ensure that the ring buffer has separate in-use region.
163 6 : for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
164 10 : microtask_queue()->EnqueueMicrotask(*NewMicrotask([] {}));
165 : }
166 1 : microtask_queue()->RunMicrotasks(isolate());
167 :
168 : std::vector<Object> expected;
169 6 : for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
170 5 : Handle<Microtask> microtask = NewMicrotask([] {});
171 10 : expected.push_back(*microtask);
172 5 : microtask_queue()->EnqueueMicrotask(*microtask);
173 : }
174 1 : EXPECT_GT(microtask_queue()->start() + microtask_queue()->size(),
175 0 : microtask_queue()->capacity());
176 :
177 : RecordingVisitor visitor;
178 1 : microtask_queue()->IterateMicrotasks(&visitor);
179 :
180 1 : std::vector<Object> actual = visitor.visited();
181 1 : std::sort(expected.begin(), expected.end());
182 1 : std::sort(actual.begin(), actual.end());
183 1 : EXPECT_EQ(expected, actual);
184 1 : }
185 :
186 : } // namespace internal
187 9075 : } // namespace v8
|