1/*
2 * Copyright 2015-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/* -*- Mode: C++; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
17#pragma once
18
19#include <atomic>
20
21#include <folly/experimental/TLRefCount.h>
22
23namespace folly {
24
25template <typename T, typename RefCount>
26class ReadMostlyMainPtr;
27template <typename T, typename RefCount>
28class ReadMostlyWeakPtr;
29template <typename T, typename RefCount>
30class ReadMostlySharedPtr;
31template <typename RefCount>
32class ReadMostlyMainPtrDeleter;
33
34using DefaultRefCount = TLRefCount;
35
36namespace detail {
37
38template <typename T, typename RefCount = DefaultRefCount>
39class ReadMostlySharedPtrCore {
40 public:
41 T* get() {
42 return ptrRaw_;
43 }
44
45 std::shared_ptr<T> getShared() {
46 return ptr_;
47 }
48
49 bool incref() {
50 return ++count_ > 0;
51 }
52
53 void decref() {
54 if (--count_ == 0) {
55 ptrRaw_ = nullptr;
56 ptr_.reset();
57
58 decrefWeak();
59 }
60 }
61
62 void increfWeak() {
63 auto value = ++weakCount_;
64 DCHECK_GT(value, 0);
65 }
66
67 void decrefWeak() {
68 if (--weakCount_ == 0) {
69 delete this;
70 }
71 }
72
73 size_t useCount() const {
74 return *count_;
75 }
76
77 ~ReadMostlySharedPtrCore() noexcept {
78 assert(*count_ == 0);
79 assert(*weakCount_ == 0);
80 }
81
82 private:
83 friend class ReadMostlyMainPtr<T, RefCount>;
84 friend class ReadMostlyMainPtrDeleter<RefCount>;
85
86 explicit ReadMostlySharedPtrCore(std::shared_ptr<T> ptr)
87 : ptrRaw_(ptr.get()), ptr_(std::move(ptr)) {}
88
89 T* ptrRaw_;
90 RefCount count_;
91 RefCount weakCount_;
92 std::shared_ptr<T> ptr_;
93};
94
95} // namespace detail
96
97template <typename T, typename RefCount = DefaultRefCount>
98class ReadMostlyMainPtr {
99 public:
100 ReadMostlyMainPtr() {}
101
102 explicit ReadMostlyMainPtr(std::shared_ptr<T> ptr) {
103 reset(std::move(ptr));
104 }
105
106 ReadMostlyMainPtr(const ReadMostlyMainPtr&) = delete;
107 ReadMostlyMainPtr& operator=(const ReadMostlyMainPtr&) = delete;
108
109 ReadMostlyMainPtr(ReadMostlyMainPtr&& other) noexcept {
110 *this = std::move(other);
111 }
112
113 ReadMostlyMainPtr& operator=(ReadMostlyMainPtr&& other) noexcept {
114 std::swap(impl_, other.impl_);
115
116 return *this;
117 }
118
119 bool operator==(const ReadMostlyMainPtr<T, RefCount>& other) const {
120 return get() == other.get();
121 }
122
123 bool operator==(T* other) const {
124 return get() == other;
125 }
126
127 bool operator==(const ReadMostlySharedPtr<T, RefCount>& other) const {
128 return get() == other.get();
129 }
130
131 ~ReadMostlyMainPtr() noexcept {
132 reset();
133 }
134
135 void reset() noexcept {
136 if (impl_) {
137 impl_->count_.useGlobal();
138 impl_->weakCount_.useGlobal();
139 impl_->decref();
140 impl_ = nullptr;
141 }
142 }
143
144 void reset(std::shared_ptr<T> ptr) {
145 reset();
146 if (ptr) {
147 impl_ = new detail::ReadMostlySharedPtrCore<T, RefCount>(std::move(ptr));
148 }
149 }
150
151 T* get() const {
152 if (impl_) {
153 return impl_->ptrRaw_;
154 } else {
155 return nullptr;
156 }
157 }
158
159 std::shared_ptr<T> getStdShared() const {
160 if (impl_) {
161 return impl_->getShared();
162 } else {
163 return {};
164 }
165 }
166
167 T& operator*() const {
168 return *get();
169 }
170
171 T* operator->() const {
172 return get();
173 }
174
175 ReadMostlySharedPtr<T, RefCount> getShared() const {
176 return ReadMostlySharedPtr<T, RefCount>(*this);
177 }
178
179 explicit operator bool() const {
180 return impl_ != nullptr;
181 }
182
183 private:
184 friend class ReadMostlyWeakPtr<T, RefCount>;
185 friend class ReadMostlySharedPtr<T, RefCount>;
186 friend class ReadMostlyMainPtrDeleter<RefCount>;
187
188 detail::ReadMostlySharedPtrCore<T, RefCount>* impl_{nullptr};
189};
190
191template <typename T, typename RefCount = DefaultRefCount>
192class ReadMostlyWeakPtr {
193 public:
194 ReadMostlyWeakPtr() {}
195
196 explicit ReadMostlyWeakPtr(const ReadMostlyMainPtr<T, RefCount>& mainPtr) {
197 reset(mainPtr.impl_);
198 }
199
200 explicit ReadMostlyWeakPtr(const ReadMostlySharedPtr<T, RefCount>& ptr) {
201 reset(ptr.impl_);
202 }
203
204 ReadMostlyWeakPtr(const ReadMostlyWeakPtr& other) {
205 *this = other;
206 }
207
208 ReadMostlyWeakPtr& operator=(const ReadMostlyWeakPtr& other) {
209 reset(other.impl_);
210 return *this;
211 }
212
213 ReadMostlyWeakPtr& operator=(const ReadMostlyMainPtr<T, RefCount>& mainPtr) {
214 reset(mainPtr.impl_);
215 return *this;
216 }
217
218 ReadMostlyWeakPtr(ReadMostlyWeakPtr&& other) noexcept {
219 *this = other;
220 }
221
222 ReadMostlyWeakPtr& operator=(ReadMostlyWeakPtr&& other) noexcept {
223 std::swap(impl_, other.impl_);
224 return *this;
225 }
226
227 ~ReadMostlyWeakPtr() noexcept {
228 reset(nullptr);
229 }
230
231 ReadMostlySharedPtr<T, RefCount> lock() {
232 return ReadMostlySharedPtr<T, RefCount>(*this);
233 }
234
235 private:
236 friend class ReadMostlySharedPtr<T, RefCount>;
237
238 void reset(detail::ReadMostlySharedPtrCore<T, RefCount>* impl) {
239 if (impl_) {
240 impl_->decrefWeak();
241 }
242 impl_ = impl;
243 if (impl_) {
244 impl_->increfWeak();
245 }
246 }
247
248 detail::ReadMostlySharedPtrCore<T, RefCount>* impl_{nullptr};
249};
250
251template <typename T, typename RefCount = DefaultRefCount>
252class ReadMostlySharedPtr {
253 public:
254 ReadMostlySharedPtr() {}
255
256 explicit ReadMostlySharedPtr(const ReadMostlyWeakPtr<T, RefCount>& weakPtr) {
257 reset(weakPtr.impl_);
258 }
259
260 // Generally, this shouldn't be used.
261 explicit ReadMostlySharedPtr(const ReadMostlyMainPtr<T, RefCount>& mainPtr) {
262 reset(mainPtr.impl_);
263 }
264
265 ReadMostlySharedPtr(const ReadMostlySharedPtr& other) {
266 *this = other;
267 }
268
269 ReadMostlySharedPtr& operator=(const ReadMostlySharedPtr& other) {
270 reset(other.impl_);
271 return *this;
272 }
273
274 ReadMostlySharedPtr& operator=(const ReadMostlyWeakPtr<T, RefCount>& other) {
275 reset(other.impl_);
276 return *this;
277 }
278
279 ReadMostlySharedPtr& operator=(const ReadMostlyMainPtr<T, RefCount>& other) {
280 reset(other.impl_);
281 return *this;
282 }
283
284 ReadMostlySharedPtr(ReadMostlySharedPtr&& other) noexcept {
285 *this = std::move(other);
286 }
287
288 ~ReadMostlySharedPtr() noexcept {
289 reset(nullptr);
290 }
291
292 ReadMostlySharedPtr& operator=(ReadMostlySharedPtr&& other) noexcept {
293 std::swap(ptr_, other.ptr_);
294 std::swap(impl_, other.impl_);
295 return *this;
296 }
297
298 bool operator==(const ReadMostlyMainPtr<T, RefCount>& other) const {
299 return get() == other.get();
300 }
301
302 bool operator==(T* other) const {
303 return get() == other;
304 }
305
306 bool operator==(const ReadMostlySharedPtr<T, RefCount>& other) const {
307 return get() == other.get();
308 }
309
310 void reset() {
311 reset(nullptr);
312 }
313
314 T* get() const {
315 return ptr_;
316 }
317
318 std::shared_ptr<T> getStdShared() const {
319 if (impl_) {
320 return impl_->getShared();
321 } else {
322 return {};
323 }
324 }
325
326 T& operator*() const {
327 return *get();
328 }
329
330 T* operator->() const {
331 return get();
332 }
333
334 size_t use_count() const {
335 return impl_->useCount();
336 }
337
338 bool unique() const {
339 return use_count() == 1;
340 }
341
342 explicit operator bool() const {
343 return impl_ != nullptr;
344 }
345
346 private:
347 friend class ReadMostlyWeakPtr<T, RefCount>;
348
349 void reset(detail::ReadMostlySharedPtrCore<T, RefCount>* impl) {
350 if (impl_) {
351 impl_->decref();
352 impl_ = nullptr;
353 ptr_ = nullptr;
354 }
355
356 if (impl && impl->incref()) {
357 impl_ = impl;
358 ptr_ = impl->get();
359 }
360 }
361
362 T* ptr_{nullptr};
363 detail::ReadMostlySharedPtrCore<T, RefCount>* impl_{nullptr};
364};
365
366/**
367 * This can be used to destroy multiple ReadMostlyMainPtrs at once.
368 */
369template <typename RefCount = DefaultRefCount>
370class ReadMostlyMainPtrDeleter {
371 public:
372 ~ReadMostlyMainPtrDeleter() noexcept {
373 RefCount::useGlobal(refCounts_);
374 for (auto& decref : decrefs_) {
375 decref();
376 }
377 }
378
379 template <typename T>
380 void add(ReadMostlyMainPtr<T, RefCount> ptr) noexcept {
381 if (!ptr.impl_) {
382 return;
383 }
384
385 refCounts_.push_back(&ptr.impl_->count_);
386 refCounts_.push_back(&ptr.impl_->weakCount_);
387 decrefs_.push_back([impl = ptr.impl_] { impl->decref(); });
388 ptr.impl_ = nullptr;
389 }
390
391 private:
392 std::vector<RefCount*> refCounts_;
393 std::vector<folly::Function<void()>> decrefs_;
394};
395
396template <typename T, typename RefCount>
397inline bool operator==(
398 const ReadMostlyMainPtr<T, RefCount>& ptr,
399 std::nullptr_t) {
400 return ptr.get() == nullptr;
401}
402
403template <typename T, typename RefCount>
404inline bool operator==(
405 std::nullptr_t,
406 const ReadMostlyMainPtr<T, RefCount>& ptr) {
407 return ptr.get() == nullptr;
408}
409
410template <typename T, typename RefCount>
411inline bool operator==(
412 const ReadMostlySharedPtr<T, RefCount>& ptr,
413 std::nullptr_t) {
414 return ptr.get() == nullptr;
415}
416
417template <typename T, typename RefCount>
418inline bool operator==(
419 std::nullptr_t,
420 const ReadMostlySharedPtr<T, RefCount>& ptr) {
421 return ptr.get() == nullptr;
422}
423
424template <typename T, typename RefCount>
425inline bool operator!=(
426 const ReadMostlyMainPtr<T, RefCount>& ptr,
427 std::nullptr_t) {
428 return !(ptr == nullptr);
429}
430
431template <typename T, typename RefCount>
432inline bool operator!=(
433 std::nullptr_t,
434 const ReadMostlyMainPtr<T, RefCount>& ptr) {
435 return !(ptr == nullptr);
436}
437
438template <typename T, typename RefCount>
439inline bool operator!=(
440 const ReadMostlySharedPtr<T, RefCount>& ptr,
441 std::nullptr_t) {
442 return !(ptr == nullptr);
443}
444
445template <typename T, typename RefCount>
446inline bool operator!=(
447 std::nullptr_t,
448 const ReadMostlySharedPtr<T, RefCount>& ptr) {
449 return !(ptr == nullptr);
450}
451} // namespace folly
452