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
31using namespace folly;
32using namespace folly::test;
33using namespace std;
34static int c_count{0};
35static int d_count{0};
36
37using DSched = DeterministicSchedule;
38
39DEFINE_int64(seed, 0, "Seed for random number generators");
40DEFINE_int32(num_threads, 32, "Number of threads");
41
42struct foo {
43 foo() {
44 c_count++;
45 }
46 ~foo() {
47 d_count++;
48 }
49};
50
51TEST(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
63TEST(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
71TEST(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
91TEST(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
112TEST(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
120TEST(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}
127TEST(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
153TEST(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
164TEST(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