Fix copyright lines
[folly.git] / folly / concurrency / CoreCachedSharedPtr.h
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 #pragma once
18
19 #include <array>
20 #include <memory>
21
22 #include <folly/concurrency/AtomicSharedPtr.h>
23 #include <folly/concurrency/CacheLocality.h>
24 #include <folly/container/Enumerate.h>
25 #include <folly/experimental/hazptr/hazptr.h>
26
27 namespace folly {
28
29 /**
30  * This class creates core-local caches for a given shared_ptr, to
31  * mitigate contention when acquiring/releasing it.
32  *
33  * It has the same thread-safety guarantees as shared_ptr: it is safe
34  * to concurrently call get(), but reset()s must be synchronized with
35  * reads and other resets().
36  *
37  * @author Giuseppe Ottaviano <ott@fb.com>
38  */
39 template <class T, size_t kNumSlots = 64>
40 class CoreCachedSharedPtr {
41  public:
42   explicit CoreCachedSharedPtr(const std::shared_ptr<T>& p = nullptr) {
43     reset(p);
44   }
45
46   void reset(const std::shared_ptr<T>& p = nullptr) {
47     // Allocate each Holder in a different CoreAllocator stripe to
48     // prevent false sharing. Their control blocks will be adjacent
49     // thanks to allocate_shared().
50     for (auto slot : folly::enumerate(slots_)) {
51       auto alloc = getCoreAllocatorStl<Holder, kNumSlots>(slot.index);
52       auto holder = std::allocate_shared<Holder>(alloc, p);
53       *slot = std::shared_ptr<T>(holder, p.get());
54     }
55   }
56
57   std::shared_ptr<T> get() const {
58     return slots_[AccessSpreader<>::current(kNumSlots)];
59   }
60
61  private:
62   using Holder = std::shared_ptr<T>;
63
64   template <class, size_t>
65   friend class CoreCachedWeakPtr;
66
67   std::array<std::shared_ptr<T>, kNumSlots> slots_;
68 };
69
70 template <class T, size_t kNumSlots = 64>
71 class CoreCachedWeakPtr {
72  public:
73   explicit CoreCachedWeakPtr(const CoreCachedSharedPtr<T, kNumSlots>& p) {
74     for (auto slot : folly::enumerate(slots_)) {
75       *slot = p.slots_[slot.index];
76     }
77   }
78
79   std::weak_ptr<T> get() const {
80     return slots_[AccessSpreader<>::current(kNumSlots)];
81   }
82
83  private:
84   std::array<std::weak_ptr<T>, kNumSlots> slots_;
85 };
86
87 /**
88  * This class creates core-local caches for a given shared_ptr, to
89  * mitigate contention when acquiring/releasing it.
90  *
91  * All methods are threadsafe.  Hazard pointers are used to avoid
92  * use-after-free for concurrent reset() and get() operations.
93  *
94  * Concurrent reset()s are sequenced with respect to each other: the
95  * sharded shared_ptrs will always all be set to the same value.
96  * get()s will never see a newer pointer on one core, and an older
97  * pointer on another after a subsequent thread migration.
98  */
99 template <class T, size_t kNumSlots = 64>
100 class AtomicCoreCachedSharedPtr {
101  public:
102   explicit AtomicCoreCachedSharedPtr(const std::shared_ptr<T>& p = nullptr) {
103     reset(p);
104   }
105
106   ~AtomicCoreCachedSharedPtr() {
107     auto slots = slots_.load(std::memory_order_acquire);
108     // Delete of AtomicCoreCachedSharedPtr must be synchronized, no
109     // need for stlots->retire().
110     if (slots) {
111       delete slots;
112     }
113   }
114
115   void reset(const std::shared_ptr<T>& p = nullptr) {
116     auto newslots = folly::make_unique<Slots>();
117     // Allocate each Holder in a different CoreAllocator stripe to
118     // prevent false sharing. Their control blocks will be adjacent
119     // thanks to allocate_shared().
120     for (auto slot : folly::enumerate(newslots->slots_)) {
121       auto alloc = getCoreAllocatorStl<Holder, kNumSlots>(slot.index);
122       auto holder = std::allocate_shared<Holder>(alloc, p);
123       *slot = std::shared_ptr<T>(holder, p.get());
124     }
125
126     auto oldslots = slots_.exchange(newslots.release());
127     if (oldslots) {
128       oldslots->retire();
129     }
130   }
131
132   std::shared_ptr<T> get() const {
133     folly::hazptr::hazptr_holder hazptr;
134     auto slots = hazptr.get_protected(slots_);
135     if (!slots) {
136       return nullptr;
137     }
138     return (slots->slots_)[AccessSpreader<>::current(kNumSlots)];
139   }
140
141  private:
142   using Holder = std::shared_ptr<T>;
143   struct Slots : folly::hazptr::hazptr_obj_base<Slots> {
144     std::array<std::shared_ptr<T>, kNumSlots> slots_;
145   };
146   std::atomic<Slots*> slots_{nullptr};
147 };
148
149 } // namespace folly