New ReadMostlySharedPtr implementation
[folly.git] / folly / experimental / test / ReadMostlySharedPtrTest.cpp
1 /*
2  * Copyright 2015 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 #include <gtest/gtest.h>
24
25 #include <folly/Baton.h>
26 #include <folly/experimental/ReadMostlySharedPtr.h>
27
28 using folly::ReadMostlyMainPtr;
29 using folly::ReadMostlyWeakPtr;
30 using folly::ReadMostlySharedPtr;
31
32 // send SIGALRM to test process after this many seconds
33 const unsigned int TEST_TIMEOUT = 10;
34
35 class ReadMostlySharedPtrTest : public ::testing::Test {
36  public:
37   ReadMostlySharedPtrTest() {
38     alarm(TEST_TIMEOUT);
39   }
40 };
41
42 struct TestObject {
43   int value;
44   std::atomic<int>& counter;
45
46   TestObject(int value, std::atomic<int>& counter)
47       : value(value), counter(counter) {
48     ++counter;
49   }
50
51   ~TestObject() {
52     assert(counter.load() > 0);
53     --counter;
54   }
55 };
56
57 // One side calls requestAndWait(), the other side calls waitForRequest(),
58 // does something and calls completed().
59 class Coordinator {
60  public:
61   void requestAndWait() {
62     requestBaton_.post();
63     completeBaton_.wait();
64   }
65
66   void waitForRequest() {
67     folly::RCURegisterThread();
68     requestBaton_.wait();
69   }
70
71   void completed() {
72     completeBaton_.post();
73   }
74
75  private:
76   folly::Baton<> requestBaton_;
77   folly::Baton<> completeBaton_;
78 };
79
80 TEST_F(ReadMostlySharedPtrTest, BasicStores) {
81   ReadMostlyMainPtr<TestObject> ptr;
82
83   // Store 1.
84   std::atomic<int> cnt1{0};
85   ptr.reset(folly::make_unique<TestObject>(1, cnt1));
86   EXPECT_EQ(1, cnt1.load());
87
88   // Store 2, check that 1 is destroyed.
89   std::atomic<int> cnt2{0};
90   ptr.reset(folly::make_unique<TestObject>(2, cnt2));
91   EXPECT_EQ(1, cnt2.load());
92   EXPECT_EQ(0, cnt1.load());
93
94   // Store nullptr, check that 2 is destroyed.
95   ptr.reset(nullptr);
96   EXPECT_EQ(0, cnt2.load());
97 }
98
99 TEST_F(ReadMostlySharedPtrTest, BasicLoads) {
100   std::atomic<int> cnt2{0};
101   ReadMostlySharedPtr<TestObject> x;
102
103   {
104     ReadMostlyMainPtr<TestObject> ptr;
105
106     // Check that ptr is initially nullptr.
107     EXPECT_EQ(ptr.get(), nullptr);
108
109     std::atomic<int> cnt1{0};
110     ptr.reset(folly::make_unique<TestObject>(1, cnt1));
111     EXPECT_EQ(1, cnt1.load());
112
113     x = ptr;
114     EXPECT_EQ(1, x->value);
115
116     ptr.reset(folly::make_unique<TestObject>(2, cnt2));
117     EXPECT_EQ(1, cnt2.load());
118     EXPECT_EQ(1, cnt1.load());
119
120     x = ptr;
121     EXPECT_EQ(2, x->value);
122     EXPECT_EQ(0, cnt1.load());
123
124     ptr.reset(nullptr);
125     EXPECT_EQ(1, cnt2.load());
126   }
127
128   EXPECT_EQ(1, cnt2.load());
129
130   x.reset();
131   EXPECT_EQ(0, cnt2.load());
132 }
133
134 TEST_F(ReadMostlySharedPtrTest, LoadsFromThreads) {
135   std::atomic<int> cnt{0};
136
137   {
138     ReadMostlyMainPtr<TestObject> ptr;
139     Coordinator loads[7];
140
141     std::thread t1([&] {
142       loads[0].waitForRequest();
143       EXPECT_EQ(ptr.getShared(), nullptr);
144       loads[0].completed();
145
146       loads[3].waitForRequest();
147       EXPECT_EQ(2, ptr.getShared()->value);
148       loads[3].completed();
149
150       loads[4].waitForRequest();
151       EXPECT_EQ(4, ptr.getShared()->value);
152       loads[4].completed();
153
154       loads[5].waitForRequest();
155       EXPECT_EQ(5, ptr.getShared()->value);
156       loads[5].completed();
157     });
158
159     std::thread t2([&] {
160       loads[1].waitForRequest();
161       EXPECT_EQ(1, ptr.getShared()->value);
162       loads[1].completed();
163
164       loads[2].waitForRequest();
165       EXPECT_EQ(2, ptr.getShared()->value);
166       loads[2].completed();
167
168       loads[6].waitForRequest();
169       EXPECT_EQ(5, ptr.getShared()->value);
170       loads[6].completed();
171     });
172
173     loads[0].requestAndWait();
174
175     ptr.reset(folly::make_unique<TestObject>(1, cnt));
176     loads[1].requestAndWait();
177
178     ptr.reset(folly::make_unique<TestObject>(2, cnt));
179     loads[2].requestAndWait();
180     loads[3].requestAndWait();
181
182     ptr.reset(folly::make_unique<TestObject>(3, cnt));
183     ptr.reset(folly::make_unique<TestObject>(4, cnt));
184     loads[4].requestAndWait();
185
186     ptr.reset(folly::make_unique<TestObject>(5, cnt));
187     loads[5].requestAndWait();
188     loads[6].requestAndWait();
189
190     EXPECT_EQ(1, cnt.load());
191
192     t1.join();
193     t2.join();
194   }
195
196   EXPECT_EQ(0, cnt.load());
197 }
198
199 TEST_F(ReadMostlySharedPtrTest, Ctor) {
200   std::atomic<int> cnt1{0};
201   {
202     ReadMostlyMainPtr<TestObject> ptr(
203       folly::make_unique<TestObject>(1, cnt1));
204
205     EXPECT_EQ(1, ptr.getShared()->value);
206   }
207
208   EXPECT_EQ(0, cnt1.load());
209 }
210
211 TEST_F(ReadMostlySharedPtrTest, ClearingCache) {
212   ReadMostlyMainPtr<TestObject> ptr;
213
214   // Store 1.
215   std::atomic<int> cnt1{0};
216   ptr.reset(folly::make_unique<TestObject>(1, cnt1));
217
218   Coordinator c;
219
220   std::thread t([&] {
221     // Cache the pointer for this thread.
222     ptr.getShared();
223     c.requestAndWait();
224   });
225
226   // Wait for the thread to cache pointer.
227   c.waitForRequest();
228   EXPECT_EQ(1, cnt1.load());
229
230   // Store 2 and check that 1 is destroyed.
231   std::atomic<int> cnt2{0};
232   ptr.reset(folly::make_unique<TestObject>(2, cnt2));
233   EXPECT_EQ(0, cnt1.load());
234
235   // Unblock thread.
236   c.completed();
237   t.join();
238 }