ReadMostlySharedPtrTest, subtle test bug caught by -fsanitize-address-use-after-scope
[folly.git] / folly / experimental / test / ReadMostlySharedPtrTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /* -*- Mode: C++; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
17
18 #include <atomic>
19 #include <thread>
20 #include <mutex>
21 #include <folly/Memory.h>
22 #include <condition_variable>
23
24 #include <folly/Baton.h>
25 #include <folly/experimental/RCURefCount.h>
26 #include <folly/experimental/ReadMostlySharedPtr.h>
27 #include <folly/portability/GTest.h>
28
29 using folly::ReadMostlyMainPtr;
30 using folly::ReadMostlyWeakPtr;
31 using folly::ReadMostlySharedPtr;
32 using folly::ReadMostlyMainPtrDeleter;
33
34 // send SIGALRM to test process after this many seconds
35 const unsigned int TEST_TIMEOUT = 10;
36
37 class ReadMostlySharedPtrTest : public ::testing::Test {
38  public:
39   ReadMostlySharedPtrTest() {
40     alarm(TEST_TIMEOUT);
41   }
42 };
43
44 struct TestObject {
45   int value;
46   std::atomic<int>& counter;
47
48   TestObject(int value, std::atomic<int>& counter)
49       : value(value), counter(counter) {
50     ++counter;
51   }
52
53   ~TestObject() {
54     assert(counter.load() > 0);
55     --counter;
56   }
57 };
58
59 // One side calls requestAndWait(), the other side calls waitForRequest(),
60 // does something and calls completed().
61 class Coordinator {
62  public:
63   void requestAndWait() {
64     requestBaton_.post();
65     completeBaton_.wait();
66   }
67
68   void waitForRequest() {
69     folly::RCURegisterThread();
70     requestBaton_.wait();
71   }
72
73   void completed() {
74     completeBaton_.post();
75   }
76
77  private:
78   folly::Baton<> requestBaton_;
79   folly::Baton<> completeBaton_;
80 };
81
82 TEST_F(ReadMostlySharedPtrTest, BasicStores) {
83   ReadMostlyMainPtr<TestObject> ptr;
84
85   // Store 1.
86   std::atomic<int> cnt1{0};
87   ptr.reset(folly::make_unique<TestObject>(1, cnt1));
88   EXPECT_EQ(1, cnt1.load());
89
90   // Store 2, check that 1 is destroyed.
91   std::atomic<int> cnt2{0};
92   ptr.reset(folly::make_unique<TestObject>(2, cnt2));
93   EXPECT_EQ(1, cnt2.load());
94   EXPECT_EQ(0, cnt1.load());
95
96   // Store nullptr, check that 2 is destroyed.
97   ptr.reset(nullptr);
98   EXPECT_EQ(0, cnt2.load());
99 }
100
101 TEST_F(ReadMostlySharedPtrTest, BasicLoads) {
102   std::atomic<int> cnt2{0};
103   ReadMostlySharedPtr<TestObject> x;
104
105   {
106     ReadMostlyMainPtr<TestObject> ptr;
107
108     // Check that ptr is initially nullptr.
109     EXPECT_EQ(ptr.get(), nullptr);
110
111     std::atomic<int> cnt1{0};
112     ptr.reset(folly::make_unique<TestObject>(1, cnt1));
113     EXPECT_EQ(1, cnt1.load());
114
115     x = ptr;
116     EXPECT_EQ(1, x->value);
117
118     ptr.reset(folly::make_unique<TestObject>(2, cnt2));
119     EXPECT_EQ(1, cnt2.load());
120     EXPECT_EQ(1, cnt1.load());
121
122     x = ptr;
123     EXPECT_EQ(2, x->value);
124     EXPECT_EQ(0, cnt1.load());
125
126     ptr.reset(nullptr);
127     EXPECT_EQ(1, cnt2.load());
128   }
129
130   EXPECT_EQ(1, cnt2.load());
131
132   x.reset();
133   EXPECT_EQ(0, cnt2.load());
134 }
135
136 TEST_F(ReadMostlySharedPtrTest, LoadsFromThreads) {
137   std::atomic<int> cnt{0};
138
139   {
140     ReadMostlyMainPtr<TestObject> ptr;
141     Coordinator loads[7];
142
143     std::thread t1([&] {
144       loads[0].waitForRequest();
145       EXPECT_EQ(ptr.getShared(), nullptr);
146       loads[0].completed();
147
148       loads[3].waitForRequest();
149       EXPECT_EQ(2, ptr.getShared()->value);
150       loads[3].completed();
151
152       loads[4].waitForRequest();
153       EXPECT_EQ(4, ptr.getShared()->value);
154       loads[4].completed();
155
156       loads[5].waitForRequest();
157       EXPECT_EQ(5, ptr.getShared()->value);
158       loads[5].completed();
159     });
160
161     std::thread t2([&] {
162       loads[1].waitForRequest();
163       EXPECT_EQ(1, ptr.getShared()->value);
164       loads[1].completed();
165
166       loads[2].waitForRequest();
167       EXPECT_EQ(2, ptr.getShared()->value);
168       loads[2].completed();
169
170       loads[6].waitForRequest();
171       EXPECT_EQ(5, ptr.getShared()->value);
172       loads[6].completed();
173     });
174
175     loads[0].requestAndWait();
176
177     ptr.reset(folly::make_unique<TestObject>(1, cnt));
178     loads[1].requestAndWait();
179
180     ptr.reset(folly::make_unique<TestObject>(2, cnt));
181     loads[2].requestAndWait();
182     loads[3].requestAndWait();
183
184     ptr.reset(folly::make_unique<TestObject>(3, cnt));
185     ptr.reset(folly::make_unique<TestObject>(4, cnt));
186     loads[4].requestAndWait();
187
188     ptr.reset(folly::make_unique<TestObject>(5, cnt));
189     loads[5].requestAndWait();
190     loads[6].requestAndWait();
191
192     EXPECT_EQ(1, cnt.load());
193
194     t1.join();
195     t2.join();
196   }
197
198   EXPECT_EQ(0, cnt.load());
199 }
200
201 TEST_F(ReadMostlySharedPtrTest, Ctor) {
202   std::atomic<int> cnt1{0};
203   {
204     ReadMostlyMainPtr<TestObject> ptr(
205       folly::make_unique<TestObject>(1, cnt1));
206
207     EXPECT_EQ(1, ptr.getShared()->value);
208   }
209
210   EXPECT_EQ(0, cnt1.load());
211 }
212
213 TEST_F(ReadMostlySharedPtrTest, ClearingCache) {
214   std::atomic<int> cnt1{0};
215   std::atomic<int> cnt2{0};
216
217   ReadMostlyMainPtr<TestObject> ptr;
218
219   // Store 1.
220   ptr.reset(folly::make_unique<TestObject>(1, cnt1));
221
222   Coordinator c;
223
224   std::thread t([&] {
225     // Cache the pointer for this thread.
226     ptr.getShared();
227     c.requestAndWait();
228   });
229
230   // Wait for the thread to cache pointer.
231   c.waitForRequest();
232   EXPECT_EQ(1, cnt1.load());
233
234   // Store 2 and check that 1 is destroyed.
235   ptr.reset(folly::make_unique<TestObject>(2, cnt2));
236   EXPECT_EQ(0, cnt1.load());
237
238   // Unblock thread.
239   c.completed();
240   t.join();
241 }
242
243 size_t useGlobalCalls = 0;
244
245 class TestRefCount {
246  public:
247   ~TestRefCount() noexcept {
248     DCHECK_EQ(count_.load(), 0);
249   }
250
251   int64_t operator++() noexcept {
252     auto ret = ++count_;
253     DCHECK_GT(ret, 0);
254     return ret;
255   }
256
257   int64_t operator--() noexcept {
258     auto ret = --count_;
259     DCHECK_GE(ret, 0);
260     return ret;
261   }
262
263   int64_t operator*() noexcept {
264     return count_.load();
265   }
266
267   void useGlobal() {
268     ++useGlobalCalls;
269   }
270
271   template <typename Container>
272   static void useGlobal(const Container&) {
273     ++useGlobalCalls;
274   }
275
276  private:
277   std::atomic<int64_t> count_{1};
278 };
279
280 TEST_F(ReadMostlySharedPtrTest, ReadMostlyMainPtrDeleter) {
281   EXPECT_EQ(0, useGlobalCalls);
282   {
283     ReadMostlyMainPtr<int, TestRefCount> ptr1(std::make_shared<int>(42));
284     ReadMostlyMainPtr<int, TestRefCount> ptr2(std::make_shared<int>(42));
285   }
286
287   EXPECT_EQ(4, useGlobalCalls);
288
289   useGlobalCalls = 0;
290   {
291     ReadMostlyMainPtr<int, TestRefCount> ptr1(std::make_shared<int>(42));
292     ReadMostlyMainPtr<int, TestRefCount> ptr2(std::make_shared<int>(42));
293
294     ReadMostlyMainPtrDeleter<TestRefCount> deleter;
295     deleter.add(std::move(ptr1));
296     deleter.add(std::move(ptr2));
297   }
298
299   EXPECT_EQ(1, useGlobalCalls);
300 }
301
302 TEST_F(ReadMostlySharedPtrTest, nullptr) {
303   {
304     ReadMostlyMainPtr<int, TestRefCount> nptr;
305     EXPECT_TRUE(nptr == nullptr);
306     EXPECT_TRUE(nullptr == nptr);
307     EXPECT_EQ(nptr, nullptr);
308     EXPECT_EQ(nullptr, nptr);
309     EXPECT_FALSE(nptr);
310     EXPECT_TRUE(!nptr);
311
312     ReadMostlyMainPtr<int, TestRefCount> ptr(std::make_shared<int>(42));
313     EXPECT_FALSE(ptr == nullptr);
314     EXPECT_FALSE(nullptr == ptr);
315     EXPECT_NE(ptr, nullptr);
316     EXPECT_NE(nullptr, ptr);
317     EXPECT_FALSE(!ptr);
318     EXPECT_TRUE(ptr);
319   }
320   {
321     ReadMostlySharedPtr<int, TestRefCount> nptr;
322     EXPECT_TRUE(nptr == nullptr);
323     EXPECT_TRUE(nullptr == nptr);
324     EXPECT_EQ(nptr, nullptr);
325     EXPECT_EQ(nullptr, nptr);
326     EXPECT_FALSE(nptr);
327     EXPECT_TRUE(!nptr);
328
329     ReadMostlyMainPtr<int, TestRefCount> ptr(std::make_shared<int>(42));
330     EXPECT_FALSE(ptr == nullptr);
331     EXPECT_FALSE(nullptr == ptr);
332     EXPECT_NE(ptr, nullptr);
333     EXPECT_NE(nullptr, ptr);
334     EXPECT_FALSE(!ptr);
335     EXPECT_TRUE(ptr);
336   }
337 }
338
339 TEST_F(ReadMostlySharedPtrTest, getStdShared) {
340   const ReadMostlyMainPtr<int> rmmp1(std::make_shared<int>(42));
341
342   ReadMostlyMainPtr<int> rmmp2;
343   rmmp2.reset(rmmp1.getStdShared());
344
345   const ReadMostlySharedPtr<int> rmsp1 = rmmp1.getShared();
346   ReadMostlySharedPtr<int> rmsp2(rmsp1);
347
348   // No conditions to check; we just wanted to ensure this compiles.
349   SUCCEED();
350 }