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 | |
17 | namespace folly { |
18 | |
19 | namespace detail { |
20 | |
21 | template <typename T> |
22 | template <typename Tag, typename VaultTag> |
23 | struct SingletonHolder<T>::Impl : SingletonHolder<T> { |
24 | Impl() |
25 | : SingletonHolder<T>( |
26 | {typeid(T), typeid(Tag)}, |
27 | *SingletonVault::singleton<VaultTag>()) {} |
28 | }; |
29 | |
30 | template <typename T> |
31 | template <typename Tag, typename VaultTag> |
32 | inline SingletonHolder<T>& SingletonHolder<T>::singleton() { |
33 | return detail::createGlobal<Impl<Tag, VaultTag>, void>(); |
34 | } |
35 | |
36 | [[noreturn]] void singletonWarnDoubleRegistrationAndAbort( |
37 | const TypeDescriptor& type); |
38 | |
39 | template <typename T> |
40 | void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) { |
41 | std::lock_guard<std::mutex> entry_lock(mutex_); |
42 | |
43 | if (state_ != SingletonHolderState::NotRegistered) { |
44 | /* Possible causes: |
45 | * |
46 | * You have two instances of the same |
47 | * folly::Singleton<Class>. Probably because you define the |
48 | * singleton in a header included in multiple places? In general, |
49 | * folly::Singleton shouldn't be in the header, only off in some |
50 | * anonymous namespace in a cpp file. Code needing the singleton |
51 | * will find it when that code references folly::Singleton<Class>. |
52 | * |
53 | * Alternatively, you could have 2 singletons with the same type |
54 | * defined with a different name in a .cpp (source) file. For |
55 | * example: |
56 | * |
57 | * Singleton<int> a([] { return new int(3); }); |
58 | * Singleton<int> b([] { return new int(4); }); |
59 | * |
60 | * Adding tags should fix this (see documentation in the header). |
61 | * |
62 | */ |
63 | singletonWarnDoubleRegistrationAndAbort(type()); |
64 | } |
65 | |
66 | create_ = std::move(c); |
67 | teardown_ = std::move(t); |
68 | |
69 | state_ = SingletonHolderState::Dead; |
70 | } |
71 | |
72 | template <typename T> |
73 | void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) { |
74 | if (state_ == SingletonHolderState::NotRegistered) { |
75 | detail::singletonWarnRegisterMockEarlyAndAbort(type()); |
76 | } |
77 | if (state_ == SingletonHolderState::Living) { |
78 | destroyInstance(); |
79 | } |
80 | |
81 | { |
82 | auto creationOrder = vault_.creationOrder_.wlock(); |
83 | |
84 | auto it = std::find(creationOrder->begin(), creationOrder->end(), type()); |
85 | if (it != creationOrder->end()) { |
86 | creationOrder->erase(it); |
87 | } |
88 | } |
89 | |
90 | std::lock_guard<std::mutex> entry_lock(mutex_); |
91 | |
92 | create_ = std::move(c); |
93 | teardown_ = std::move(t); |
94 | } |
95 | |
96 | template <typename T> |
97 | T* SingletonHolder<T>::get() { |
98 | if (LIKELY( |
99 | state_.load(std::memory_order_acquire) == |
100 | SingletonHolderState::Living)) { |
101 | return instance_ptr_; |
102 | } |
103 | createInstance(); |
104 | |
105 | if (instance_weak_.expired()) { |
106 | detail::singletonThrowGetInvokedAfterDestruction(type()); |
107 | } |
108 | |
109 | return instance_ptr_; |
110 | } |
111 | |
112 | template <typename T> |
113 | std::weak_ptr<T> SingletonHolder<T>::get_weak() { |
114 | if (UNLIKELY( |
115 | state_.load(std::memory_order_acquire) != |
116 | SingletonHolderState::Living)) { |
117 | createInstance(); |
118 | } |
119 | |
120 | return instance_weak_; |
121 | } |
122 | |
123 | template <typename T> |
124 | std::shared_ptr<T> SingletonHolder<T>::try_get() { |
125 | if (UNLIKELY( |
126 | state_.load(std::memory_order_acquire) != |
127 | SingletonHolderState::Living)) { |
128 | createInstance(); |
129 | } |
130 | |
131 | return instance_weak_.lock(); |
132 | } |
133 | |
134 | template <typename T> |
135 | folly::ReadMostlySharedPtr<T> SingletonHolder<T>::try_get_fast() { |
136 | if (UNLIKELY( |
137 | state_.load(std::memory_order_acquire) != |
138 | SingletonHolderState::Living)) { |
139 | createInstance(); |
140 | } |
141 | |
142 | return instance_weak_fast_.lock(); |
143 | } |
144 | |
145 | template <typename T> |
146 | void SingletonHolder<T>::vivify() { |
147 | if (UNLIKELY( |
148 | state_.load(std::memory_order_relaxed) != |
149 | SingletonHolderState::Living)) { |
150 | createInstance(); |
151 | } |
152 | } |
153 | |
154 | template <typename T> |
155 | bool SingletonHolder<T>::hasLiveInstance() { |
156 | return !instance_weak_.expired(); |
157 | } |
158 | |
159 | template <typename T> |
160 | void SingletonHolder<T>::preDestroyInstance( |
161 | ReadMostlyMainPtrDeleter<>& deleter) { |
162 | instance_copy_ = instance_; |
163 | deleter.add(std::move(instance_)); |
164 | } |
165 | |
166 | template <typename T> |
167 | void SingletonHolder<T>::destroyInstance() { |
168 | state_ = SingletonHolderState::Dead; |
169 | instance_.reset(); |
170 | instance_copy_.reset(); |
171 | if (destroy_baton_) { |
172 | constexpr std::chrono::seconds kDestroyWaitTime{5}; |
173 | auto last_reference_released = |
174 | destroy_baton_->try_wait_for(kDestroyWaitTime); |
175 | if (last_reference_released) { |
176 | teardown_(instance_ptr_); |
177 | } else { |
178 | print_destructor_stack_trace_->store(true); |
179 | detail::singletonWarnDestroyInstanceLeak(type(), instance_ptr_); |
180 | } |
181 | } |
182 | } |
183 | |
184 | template <typename T> |
185 | SingletonHolder<T>::SingletonHolder( |
186 | TypeDescriptor typeDesc, |
187 | SingletonVault& vault) |
188 | : SingletonHolderBase(typeDesc), vault_(vault) {} |
189 | |
190 | template <typename T> |
191 | bool SingletonHolder<T>::creationStarted() { |
192 | // If alive, then creation was of course started. |
193 | // This is flipped after creating_thread_ was set, and before it was reset. |
194 | if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { |
195 | return true; |
196 | } |
197 | |
198 | // Not yet built. Is it currently in progress? |
199 | if (creating_thread_.load(std::memory_order_acquire) != std::thread::id()) { |
200 | return true; |
201 | } |
202 | |
203 | return false; |
204 | } |
205 | |
206 | template <typename T> |
207 | void SingletonHolder<T>::createInstance() { |
208 | if (creating_thread_.load(std::memory_order_acquire) == |
209 | std::this_thread::get_id()) { |
210 | detail::singletonWarnCreateCircularDependencyAndAbort(type()); |
211 | } |
212 | |
213 | std::lock_guard<std::mutex> entry_lock(mutex_); |
214 | if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { |
215 | return; |
216 | } |
217 | if (state_.load(std::memory_order_acquire) == |
218 | SingletonHolderState::NotRegistered) { |
219 | detail::singletonWarnCreateUnregisteredAndAbort(type()); |
220 | } |
221 | |
222 | if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { |
223 | return; |
224 | } |
225 | |
226 | SCOPE_EXIT { |
227 | // Clean up creator thread when complete, and also, in case of errors here, |
228 | // so that subsequent attempts don't think this is still in the process of |
229 | // being built. |
230 | creating_thread_.store(std::thread::id(), std::memory_order_release); |
231 | }; |
232 | |
233 | creating_thread_.store(std::this_thread::get_id(), std::memory_order_release); |
234 | |
235 | auto state = vault_.state_.rlock(); |
236 | if (vault_.type_ != SingletonVault::Type::Relaxed && |
237 | !state->registrationComplete) { |
238 | detail::singletonWarnCreateBeforeRegistrationCompleteAndAbort(type()); |
239 | } |
240 | if (state->state == detail::SingletonVaultState::Type::Quiescing) { |
241 | return; |
242 | } |
243 | |
244 | auto destroy_baton = std::make_shared<folly::Baton<>>(); |
245 | auto print_destructor_stack_trace = |
246 | std::make_shared<std::atomic<bool>>(false); |
247 | |
248 | // Can't use make_shared -- no support for a custom deleter, sadly. |
249 | std::shared_ptr<T> instance( |
250 | create_(), |
251 | [destroy_baton, print_destructor_stack_trace, type = type()](T*) mutable { |
252 | destroy_baton->post(); |
253 | if (print_destructor_stack_trace->load()) { |
254 | detail::singletonPrintDestructionStackTrace(type); |
255 | } |
256 | }); |
257 | |
258 | // We should schedule destroyInstances() only after the singleton was |
259 | // created. This will ensure it will be destroyed before singletons, |
260 | // not managed by folly::Singleton, which were initialized in its |
261 | // constructor |
262 | SingletonVault::scheduleDestroyInstances(); |
263 | |
264 | instance_weak_ = instance; |
265 | instance_ptr_ = instance.get(); |
266 | instance_.reset(std::move(instance)); |
267 | instance_weak_fast_ = instance_; |
268 | |
269 | destroy_baton_ = std::move(destroy_baton); |
270 | print_destructor_stack_trace_ = std::move(print_destructor_stack_trace); |
271 | |
272 | // This has to be the last step, because once state is Living other threads |
273 | // may access instance and instance_weak w/o synchronization. |
274 | state_.store(SingletonHolderState::Living, std::memory_order_release); |
275 | |
276 | vault_.creationOrder_.wlock()->push_back(type()); |
277 | } |
278 | |
279 | } // namespace detail |
280 | |
281 | } // namespace folly |
282 | |