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 5 : 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 15188 : 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 2 : EXPECT_EQ(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 15188 : 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 2 : EXPECT_EQ(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 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1,
126 0 : microtask_queue()->RunMicrotasks(isolate()));
127 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 2, count);
128 1 : }
129 :
130 : // MicrotaskQueue instances form a doubly linked list.
131 15188 : TEST_F(MicrotaskQueueTest, InstanceChain) {
132 1 : ClearTestMicrotaskQueue();
133 :
134 1 : MicrotaskQueue* default_mtq = isolate()->default_microtask_queue();
135 2 : ASSERT_TRUE(default_mtq);
136 7 : EXPECT_EQ(default_mtq, default_mtq->next());
137 3 : EXPECT_EQ(default_mtq, default_mtq->prev());
138 :
139 : // Create two instances, and check their connection.
140 : // The list contains all instances in the creation order, and the next of the
141 : // last instance is the first instance:
142 : // default_mtq -> mtq1 -> mtq2 -> default_mtq.
143 1 : std::unique_ptr<MicrotaskQueue> mtq1 = MicrotaskQueue::New(isolate());
144 1 : std::unique_ptr<MicrotaskQueue> mtq2 = MicrotaskQueue::New(isolate());
145 3 : EXPECT_EQ(default_mtq->next(), mtq1.get());
146 3 : EXPECT_EQ(mtq1->next(), mtq2.get());
147 2 : EXPECT_EQ(mtq2->next(), default_mtq);
148 2 : EXPECT_EQ(default_mtq, mtq1->prev());
149 3 : EXPECT_EQ(mtq1.get(), mtq2->prev());
150 4 : EXPECT_EQ(mtq2.get(), default_mtq->prev());
151 :
152 : // Deleted item should be also removed from the list.
153 : mtq1 = nullptr;
154 3 : EXPECT_EQ(default_mtq->next(), mtq2.get());
155 2 : EXPECT_EQ(mtq2->next(), default_mtq);
156 2 : EXPECT_EQ(default_mtq, mtq2->prev());
157 4 : EXPECT_EQ(mtq2.get(), default_mtq->prev());
158 : }
159 :
160 : // Pending Microtasks in MicrotaskQueues are strong roots. Ensure they are
161 : // visited exactly once.
162 15188 : TEST_F(MicrotaskQueueTest, VisitRoot) {
163 : // Ensure that the ring buffer has separate in-use region.
164 6 : for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
165 10 : microtask_queue()->EnqueueMicrotask(*NewMicrotask([] {}));
166 : }
167 2 : EXPECT_EQ(MicrotaskQueue::kMinimumCapacity / 2 + 1,
168 0 : microtask_queue()->RunMicrotasks(isolate()));
169 :
170 : std::vector<Object> expected;
171 6 : for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
172 5 : Handle<Microtask> microtask = NewMicrotask([] {});
173 10 : expected.push_back(*microtask);
174 5 : microtask_queue()->EnqueueMicrotask(*microtask);
175 : }
176 1 : EXPECT_GT(microtask_queue()->start() + microtask_queue()->size(),
177 0 : microtask_queue()->capacity());
178 :
179 : RecordingVisitor visitor;
180 1 : microtask_queue()->IterateMicrotasks(&visitor);
181 :
182 1 : std::vector<Object> actual = visitor.visited();
183 1 : std::sort(expected.begin(), expected.end());
184 1 : std::sort(actual.begin(), actual.end());
185 1 : EXPECT_EQ(expected, actual);
186 1 : }
187 :
188 : } // namespace internal
189 9111 : } // namespace v8
|