1 | /* |
2 | * Copyright 2017-present 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 | |
17 | // AtomicSharedPtr-detail.h only works with libstdc++, so skip these tests for |
18 | // other vendors |
19 | #ifdef FOLLY_USE_LIBSTDCPP |
20 | |
21 | #include <atomic> |
22 | #include <memory> |
23 | #include <thread> |
24 | |
25 | #include <folly/concurrency/AtomicSharedPtr.h> |
26 | #include <folly/concurrency/test/AtomicSharedPtrCounted.h> |
27 | #include <folly/portability/GTest.h> |
28 | |
29 | #include <folly/test/DeterministicSchedule.h> |
30 | |
31 | using namespace folly; |
32 | using namespace folly::test; |
33 | using namespace std; |
34 | static int c_count{0}; |
35 | static int d_count{0}; |
36 | |
37 | using DSched = DeterministicSchedule; |
38 | |
39 | DEFINE_int64(seed, 0, "Seed for random number generators" ); |
40 | DEFINE_int32(num_threads, 32, "Number of threads" ); |
41 | |
42 | struct foo { |
43 | foo() { |
44 | c_count++; |
45 | } |
46 | ~foo() { |
47 | d_count++; |
48 | } |
49 | }; |
50 | |
51 | TEST(AtomicSharedPtr, operators) { |
52 | atomic_shared_ptr<int> fooptr; |
53 | EXPECT_TRUE(fooptr.is_lock_free()); |
54 | auto i = new int(5); |
55 | std::shared_ptr<int> s(i); |
56 | fooptr.store(s); |
57 | shared_ptr<int> bar(fooptr); |
58 | EXPECT_TRUE(fooptr.compare_exchange_strong(s, nullptr)); |
59 | s.reset(); |
60 | bar.reset(); |
61 | } |
62 | |
63 | TEST(AtomicSharedPtr, exchange) { |
64 | atomic_shared_ptr<int> fooptr; |
65 | auto a = make_shared<int>(1); |
66 | fooptr.store(std::move(a)); |
67 | auto b = fooptr.exchange(make_shared<int>()); |
68 | EXPECT_EQ(*b, 1); |
69 | } |
70 | |
71 | TEST(AtomicSharedPtr, foo) { |
72 | c_count = 0; |
73 | d_count = 0; |
74 | { |
75 | atomic_shared_ptr<foo> fooptr; |
76 | fooptr.store(make_shared<foo>()); |
77 | EXPECT_EQ(1, c_count); |
78 | EXPECT_EQ(0, d_count); |
79 | { |
80 | auto res = fooptr.load(); |
81 | EXPECT_EQ(1, c_count); |
82 | EXPECT_EQ(0, d_count); |
83 | } |
84 | EXPECT_EQ(1, c_count); |
85 | EXPECT_EQ(0, d_count); |
86 | } |
87 | EXPECT_EQ(1, c_count); |
88 | EXPECT_EQ(1, d_count); |
89 | } |
90 | |
91 | TEST(AtomicSharedPtr, counted) { |
92 | c_count = 0; |
93 | d_count = 0; |
94 | { |
95 | atomic_shared_ptr<foo, std::atomic, counted_ptr_internals<std::atomic>> |
96 | fooptr; |
97 | fooptr.store(make_counted<std::atomic, foo>()); |
98 | EXPECT_EQ(1, c_count); |
99 | EXPECT_EQ(0, d_count); |
100 | { |
101 | auto res = fooptr.load(); |
102 | EXPECT_EQ(1, c_count); |
103 | EXPECT_EQ(0, d_count); |
104 | } |
105 | EXPECT_EQ(1, c_count); |
106 | EXPECT_EQ(0, d_count); |
107 | } |
108 | EXPECT_EQ(1, c_count); |
109 | EXPECT_EQ(1, d_count); |
110 | } |
111 | |
112 | TEST(AtomicSharedPtr, counted2) { |
113 | auto foo = make_counted<std::atomic, bool>(); |
114 | atomic_shared_ptr<bool, std::atomic, counted_ptr_internals<std::atomic>> |
115 | fooptr(foo); |
116 | fooptr.store(foo); |
117 | fooptr.load(); |
118 | } |
119 | |
120 | TEST(AtomicSharedPtr, ConstTest) { |
121 | const auto a(std::make_shared<foo>()); |
122 | atomic_shared_ptr<foo> atom; |
123 | atom.store(a); |
124 | |
125 | atomic_shared_ptr<const foo> catom; |
126 | } |
127 | TEST(AtomicSharedPtr, AliasingConstructorTest) { |
128 | c_count = 0; |
129 | d_count = 0; |
130 | auto a = std::make_shared<foo>(); |
131 | auto b = new foo; |
132 | auto alias = std::shared_ptr<foo>(a, b); |
133 | |
134 | atomic_shared_ptr<foo> asp; |
135 | asp.store(alias); |
136 | a.reset(); |
137 | alias.reset(); |
138 | auto res1 = asp.load(); |
139 | auto res2 = asp.exchange(nullptr); |
140 | EXPECT_EQ(b, res1.get()); |
141 | EXPECT_EQ(b, res2.get()); |
142 | EXPECT_EQ(2, c_count); |
143 | EXPECT_EQ(0, d_count); |
144 | res1.reset(); |
145 | res2.reset(); |
146 | EXPECT_EQ(2, c_count); |
147 | EXPECT_EQ(1, d_count); |
148 | delete b; |
149 | EXPECT_EQ(2, c_count); |
150 | EXPECT_EQ(2, d_count); |
151 | } |
152 | |
153 | TEST(AtomicSharedPtr, MaxPtrs) { |
154 | shared_ptr<long> p(new long); |
155 | int max_atomic_shared_ptrs = 262144; |
156 | atomic_shared_ptr<long> ptrs[max_atomic_shared_ptrs]; |
157 | for (int i = 0; i < max_atomic_shared_ptrs - 1; i++) { |
158 | ptrs[i].store(p); |
159 | } |
160 | atomic_shared_ptr<long> fail; |
161 | EXPECT_DEATH(fail.store(p), "" ); |
162 | } |
163 | |
164 | TEST(AtomicSharedPtr, DeterministicTest) { |
165 | DSched sched(DSched::uniform(FLAGS_seed)); |
166 | |
167 | auto foo = make_counted<DeterministicAtomic, bool>(); |
168 | atomic_shared_ptr< |
169 | bool, |
170 | DeterministicAtomic, |
171 | counted_ptr_internals<DeterministicAtomic>> |
172 | fooptr(foo); |
173 | std::vector<std::thread> threads(FLAGS_num_threads); |
174 | for (int tid = 0; tid < FLAGS_num_threads; ++tid) { |
175 | threads[tid] = DSched::thread([&]() { |
176 | for (int i = 0; i < 1000; i++) { |
177 | auto l = fooptr.load(); |
178 | EXPECT_TRUE(l.get() != nullptr); |
179 | fooptr.compare_exchange_strong(l, l); |
180 | fooptr.store(make_counted<DeterministicAtomic, bool>()); |
181 | EXPECT_FALSE(fooptr.compare_exchange_strong( |
182 | l, make_counted<DeterministicAtomic, bool>())); |
183 | } |
184 | }); |
185 | } |
186 | for (auto& t : threads) { |
187 | DSched::join(t); |
188 | } |
189 | } |
190 | #endif // #ifdef FOLLY_USE_LIBSTDCPP |
191 | |