1 | // Copyright 2013 The Flutter 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 | #define FML_USED_ON_EMBEDDER |
6 | |
7 | #include "flutter/fml/memory/weak_ptr.h" |
8 | |
9 | #include <thread> |
10 | #include <utility> |
11 | |
12 | #include "flutter/fml/message_loop.h" |
13 | #include "flutter/fml/raster_thread_merger.h" |
14 | #include "flutter/fml/synchronization/count_down_latch.h" |
15 | #include "gtest/gtest.h" |
16 | |
17 | namespace fml { |
18 | namespace { |
19 | |
20 | TEST(WeakPtrTest, Basic) { |
21 | int data = 0; |
22 | WeakPtrFactory<int> factory(&data); |
23 | WeakPtr<int> ptr = factory.GetWeakPtr(); |
24 | EXPECT_EQ(&data, ptr.get()); |
25 | } |
26 | |
27 | TEST(WeakPtrTest, CopyConstruction) { |
28 | int data = 0; |
29 | WeakPtrFactory<int> factory(&data); |
30 | WeakPtr<int> ptr = factory.GetWeakPtr(); |
31 | WeakPtr<int> ptr2(ptr); |
32 | EXPECT_EQ(&data, ptr.get()); |
33 | EXPECT_EQ(&data, ptr2.get()); |
34 | } |
35 | |
36 | TEST(WeakPtrTest, MoveConstruction) { |
37 | int data = 0; |
38 | WeakPtrFactory<int> factory(&data); |
39 | WeakPtr<int> ptr = factory.GetWeakPtr(); |
40 | WeakPtr<int> ptr2(std::move(ptr)); |
41 | // The clang linter flags the method called on the moved-from reference, but |
42 | // this is testing the move implementation, so it is marked NOLINT. |
43 | EXPECT_EQ(nullptr, ptr.get()); // NOLINT |
44 | EXPECT_EQ(&data, ptr2.get()); |
45 | } |
46 | |
47 | TEST(WeakPtrTest, CopyAssignment) { |
48 | int data = 0; |
49 | WeakPtrFactory<int> factory(&data); |
50 | WeakPtr<int> ptr = factory.GetWeakPtr(); |
51 | WeakPtr<int> ptr2; |
52 | EXPECT_EQ(nullptr, ptr2.get()); |
53 | ptr2 = ptr; |
54 | EXPECT_EQ(&data, ptr.get()); |
55 | EXPECT_EQ(&data, ptr2.get()); |
56 | } |
57 | |
58 | TEST(WeakPtrTest, MoveAssignment) { |
59 | int data = 0; |
60 | WeakPtrFactory<int> factory(&data); |
61 | WeakPtr<int> ptr = factory.GetWeakPtr(); |
62 | WeakPtr<int> ptr2; |
63 | EXPECT_EQ(nullptr, ptr2.get()); |
64 | ptr2 = std::move(ptr); |
65 | // The clang linter flags the method called on the moved-from reference, but |
66 | // this is testing the move implementation, so it is marked NOLINT. |
67 | EXPECT_EQ(nullptr, ptr.get()); // NOLINT |
68 | EXPECT_EQ(&data, ptr2.get()); |
69 | } |
70 | |
71 | TEST(WeakPtrTest, Testable) { |
72 | int data = 0; |
73 | WeakPtrFactory<int> factory(&data); |
74 | WeakPtr<int> ptr; |
75 | EXPECT_EQ(nullptr, ptr.get()); |
76 | EXPECT_FALSE(ptr); |
77 | ptr = factory.GetWeakPtr(); |
78 | EXPECT_EQ(&data, ptr.get()); |
79 | EXPECT_TRUE(ptr); |
80 | } |
81 | |
82 | TEST(WeakPtrTest, OutOfScope) { |
83 | WeakPtr<int> ptr; |
84 | EXPECT_EQ(nullptr, ptr.get()); |
85 | { |
86 | int data = 0; |
87 | WeakPtrFactory<int> factory(&data); |
88 | ptr = factory.GetWeakPtr(); |
89 | } |
90 | EXPECT_EQ(nullptr, ptr.get()); |
91 | } |
92 | |
93 | TEST(WeakPtrTest, Multiple) { |
94 | WeakPtr<int> a; |
95 | WeakPtr<int> b; |
96 | { |
97 | int data = 0; |
98 | WeakPtrFactory<int> factory(&data); |
99 | a = factory.GetWeakPtr(); |
100 | b = factory.GetWeakPtr(); |
101 | EXPECT_EQ(&data, a.get()); |
102 | EXPECT_EQ(&data, b.get()); |
103 | } |
104 | EXPECT_EQ(nullptr, a.get()); |
105 | EXPECT_EQ(nullptr, b.get()); |
106 | } |
107 | |
108 | TEST(WeakPtrTest, MultipleStaged) { |
109 | WeakPtr<int> a; |
110 | { |
111 | int data = 0; |
112 | WeakPtrFactory<int> factory(&data); |
113 | a = factory.GetWeakPtr(); |
114 | { WeakPtr<int> b = factory.GetWeakPtr(); } |
115 | EXPECT_NE(a.get(), nullptr); |
116 | } |
117 | EXPECT_EQ(nullptr, a.get()); |
118 | } |
119 | |
120 | struct Base { |
121 | double member = 0.; |
122 | }; |
123 | struct Derived : public Base {}; |
124 | |
125 | TEST(WeakPtrTest, Dereference) { |
126 | Base data; |
127 | data.member = 123456.; |
128 | WeakPtrFactory<Base> factory(&data); |
129 | WeakPtr<Base> ptr = factory.GetWeakPtr(); |
130 | EXPECT_EQ(&data, ptr.get()); |
131 | EXPECT_EQ(data.member, (*ptr).member); |
132 | EXPECT_EQ(data.member, ptr->member); |
133 | } |
134 | |
135 | TEST(WeakPtrTest, UpcastCopyConstruction) { |
136 | Derived data; |
137 | WeakPtrFactory<Derived> factory(&data); |
138 | WeakPtr<Derived> ptr = factory.GetWeakPtr(); |
139 | WeakPtr<Base> ptr2(ptr); |
140 | EXPECT_EQ(&data, ptr.get()); |
141 | EXPECT_EQ(&data, ptr2.get()); |
142 | } |
143 | |
144 | TEST(WeakPtrTest, UpcastMoveConstruction) { |
145 | Derived data; |
146 | WeakPtrFactory<Derived> factory(&data); |
147 | WeakPtr<Derived> ptr = factory.GetWeakPtr(); |
148 | WeakPtr<Base> ptr2(std::move(ptr)); |
149 | EXPECT_EQ(nullptr, ptr.get()); |
150 | EXPECT_EQ(&data, ptr2.get()); |
151 | } |
152 | |
153 | TEST(WeakPtrTest, UpcastCopyAssignment) { |
154 | Derived data; |
155 | WeakPtrFactory<Derived> factory(&data); |
156 | WeakPtr<Derived> ptr = factory.GetWeakPtr(); |
157 | WeakPtr<Base> ptr2; |
158 | EXPECT_EQ(nullptr, ptr2.get()); |
159 | ptr2 = ptr; |
160 | EXPECT_EQ(&data, ptr.get()); |
161 | EXPECT_EQ(&data, ptr2.get()); |
162 | } |
163 | |
164 | TEST(WeakPtrTest, UpcastMoveAssignment) { |
165 | Derived data; |
166 | WeakPtrFactory<Derived> factory(&data); |
167 | WeakPtr<Derived> ptr = factory.GetWeakPtr(); |
168 | WeakPtr<Base> ptr2; |
169 | EXPECT_EQ(nullptr, ptr2.get()); |
170 | ptr2 = std::move(ptr); |
171 | EXPECT_EQ(nullptr, ptr.get()); |
172 | EXPECT_EQ(&data, ptr2.get()); |
173 | } |
174 | |
175 | TEST(TaskRunnerAffineWeakPtrTest, ShouldNotCrashIfRunningOnTheSameTaskRunner) { |
176 | fml::MessageLoop* loop1 = nullptr; |
177 | fml::AutoResetWaitableEvent latch1; |
178 | fml::AutoResetWaitableEvent term1; |
179 | std::thread thread1([&loop1, &latch1, &term1]() { |
180 | fml::MessageLoop::EnsureInitializedForCurrentThread(); |
181 | loop1 = &fml::MessageLoop::GetCurrent(); |
182 | latch1.Signal(); |
183 | term1.Wait(); |
184 | }); |
185 | |
186 | fml::MessageLoop* loop2 = nullptr; |
187 | fml::AutoResetWaitableEvent latch2; |
188 | fml::AutoResetWaitableEvent term2; |
189 | fml::AutoResetWaitableEvent loop2_task_finish_latch; |
190 | fml::AutoResetWaitableEvent loop2_task_start_latch; |
191 | std::thread thread2([&loop2, &latch2, &term2, &loop2_task_finish_latch, |
192 | &loop2_task_start_latch]() { |
193 | fml::MessageLoop::EnsureInitializedForCurrentThread(); |
194 | int data = 0; |
195 | TaskRunnerAffineWeakPtrFactory<int> factory(&data); |
196 | loop2 = &fml::MessageLoop::GetCurrent(); |
197 | |
198 | loop2->GetTaskRunner()->PostTask([&]() { |
199 | latch2.Signal(); |
200 | loop2_task_start_latch.Wait(); |
201 | TaskRunnerAffineWeakPtr<int> ptr = factory.GetWeakPtr(); |
202 | EXPECT_EQ(*ptr, data); |
203 | loop2_task_finish_latch.Signal(); |
204 | }); |
205 | loop2->Run(); |
206 | term2.Wait(); |
207 | }); |
208 | |
209 | latch1.Wait(); |
210 | latch2.Wait(); |
211 | fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); |
212 | fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); |
213 | const auto raster_thread_merger_ = |
214 | fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
215 | const int kNumFramesMerged = 5; |
216 | |
217 | raster_thread_merger_->MergeWithLease(kNumFramesMerged); |
218 | |
219 | loop2_task_start_latch.Signal(); |
220 | loop2_task_finish_latch.Wait(); |
221 | |
222 | for (int i = 0; i < kNumFramesMerged; i++) { |
223 | ASSERT_TRUE(raster_thread_merger_->IsMerged()); |
224 | raster_thread_merger_->DecrementLease(); |
225 | } |
226 | |
227 | ASSERT_FALSE(raster_thread_merger_->IsMerged()); |
228 | loop2->Terminate(); |
229 | |
230 | term1.Signal(); |
231 | term2.Signal(); |
232 | thread1.join(); |
233 | thread2.join(); |
234 | } |
235 | |
236 | } // namespace |
237 | } // namespace fml |
238 | |