1 | /* |
2 | * Copyright (c) Facebook, Inc. and its affiliates. |
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 | template <typename Func> |
147 | invoke_result_t<Func, T*> detail::SingletonHolder<T>::apply(Func f) { |
148 | return f(try_get().get()); |
149 | } |
150 | |
151 | template <typename T> |
152 | void SingletonHolder<T>::vivify() { |
153 | if (UNLIKELY( |
154 | state_.load(std::memory_order_relaxed) != |
155 | SingletonHolderState::Living)) { |
156 | createInstance(); |
157 | } |
158 | } |
159 | |
160 | template <typename T> |
161 | bool SingletonHolder<T>::hasLiveInstance() { |
162 | return !instance_weak_.expired(); |
163 | } |
164 | |
165 | template <typename T> |
166 | void SingletonHolder<T>::preDestroyInstance( |
167 | ReadMostlyMainPtrDeleter<>& deleter) { |
168 | instance_copy_ = instance_; |
169 | deleter.add(std::move(instance_)); |
170 | } |
171 | |
172 | template <typename T> |
173 | void SingletonHolder<T>::destroyInstance() { |
174 | state_ = SingletonHolderState::Dead; |
175 | instance_.reset(); |
176 | instance_copy_.reset(); |
177 | if (destroy_baton_) { |
178 | constexpr std::chrono::seconds kDestroyWaitTime{5}; |
179 | auto last_reference_released = |
180 | destroy_baton_->try_wait_for(kDestroyWaitTime); |
181 | if (last_reference_released) { |
182 | teardown_(instance_ptr_); |
183 | } else { |
184 | print_destructor_stack_trace_->store(true); |
185 | detail::singletonWarnDestroyInstanceLeak(type(), instance_ptr_); |
186 | } |
187 | } |
188 | } |
189 | |
190 | template <typename T> |
191 | SingletonHolder<T>::SingletonHolder( |
192 | TypeDescriptor typeDesc, |
193 | SingletonVault& vault) noexcept |
194 | : SingletonHolderBase(typeDesc), vault_(vault) {} |
195 | |
196 | template <typename T> |
197 | bool SingletonHolder<T>::creationStarted() { |
198 | // If alive, then creation was of course started. |
199 | // This is flipped after creating_thread_ was set, and before it was reset. |
200 | if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { |
201 | return true; |
202 | } |
203 | |
204 | // Not yet built. Is it currently in progress? |
205 | if (creating_thread_.load(std::memory_order_acquire) != std::thread::id()) { |
206 | return true; |
207 | } |
208 | |
209 | return false; |
210 | } |
211 | |
212 | template <typename T> |
213 | void SingletonHolder<T>::createInstance() { |
214 | if (creating_thread_.load(std::memory_order_acquire) == |
215 | std::this_thread::get_id()) { |
216 | detail::singletonWarnCreateCircularDependencyAndAbort(type()); |
217 | } |
218 | |
219 | std::lock_guard<std::mutex> entry_lock(mutex_); |
220 | if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { |
221 | return; |
222 | } |
223 | if (state_.load(std::memory_order_acquire) == |
224 | SingletonHolderState::NotRegistered) { |
225 | detail::singletonWarnCreateUnregisteredAndAbort(type()); |
226 | } |
227 | |
228 | if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) { |
229 | return; |
230 | } |
231 | |
232 | SCOPE_EXIT { |
233 | // Clean up creator thread when complete, and also, in case of errors here, |
234 | // so that subsequent attempts don't think this is still in the process of |
235 | // being built. |
236 | creating_thread_.store(std::thread::id(), std::memory_order_release); |
237 | }; |
238 | |
239 | creating_thread_.store(std::this_thread::get_id(), std::memory_order_release); |
240 | |
241 | auto state = vault_.state_.rlock(); |
242 | if (vault_.type_ != SingletonVault::Type::Relaxed && |
243 | !state->registrationComplete) { |
244 | detail::singletonWarnCreateBeforeRegistrationCompleteAndAbort(type()); |
245 | } |
246 | if (state->state == detail::SingletonVaultState::Type::Quiescing) { |
247 | return; |
248 | } |
249 | |
250 | auto destroy_baton = std::make_shared<folly::Baton<>>(); |
251 | auto print_destructor_stack_trace = |
252 | std::make_shared<std::atomic<bool>>(false); |
253 | |
254 | // Can't use make_shared -- no support for a custom deleter, sadly. |
255 | std::shared_ptr<T> instance( |
256 | create_(), |
257 | [destroy_baton, print_destructor_stack_trace, type = type()](T*) mutable { |
258 | destroy_baton->post(); |
259 | if (print_destructor_stack_trace->load()) { |
260 | detail::singletonPrintDestructionStackTrace(type); |
261 | } |
262 | }); |
263 | |
264 | // We should schedule destroyInstances() only after the singleton was |
265 | // created. This will ensure it will be destroyed before singletons, |
266 | // not managed by folly::Singleton, which were initialized in its |
267 | // constructor |
268 | SingletonVault::scheduleDestroyInstances(); |
269 | |
270 | instance_weak_ = instance; |
271 | instance_ptr_ = instance.get(); |
272 | instance_.reset(std::move(instance)); |
273 | instance_weak_fast_ = instance_; |
274 | |
275 | destroy_baton_ = std::move(destroy_baton); |
276 | print_destructor_stack_trace_ = std::move(print_destructor_stack_trace); |
277 | |
278 | // This has to be the last step, because once state is Living other threads |
279 | // may access instance and instance_weak w/o synchronization. |
280 | state_.store(SingletonHolderState::Living, std::memory_order_release); |
281 | |
282 | vault_.creationOrder_.wlock()->push_back(type()); |
283 | } |
284 | |
285 | } // namespace detail |
286 | |
287 | } // namespace folly |
288 | |