1 | /* |
2 | * Copyright 2014-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 | #include <folly/Singleton.h> |
18 | #include <folly/portability/Config.h> |
19 | |
20 | #ifndef _WIN32 |
21 | #include <dlfcn.h> |
22 | #endif |
23 | |
24 | #include <atomic> |
25 | #include <cstdio> |
26 | #include <cstdlib> |
27 | #include <iostream> |
28 | #include <string> |
29 | |
30 | #include <folly/Demangle.h> |
31 | #include <folly/Format.h> |
32 | #include <folly/ScopeGuard.h> |
33 | #include <folly/detail/SingletonStackTrace.h> |
34 | |
35 | #if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__) |
36 | #define FOLLY_SINGLETON_HAVE_DLSYM 1 |
37 | #endif |
38 | |
39 | namespace folly { |
40 | |
41 | #if FOLLY_SINGLETON_HAVE_DLSYM |
42 | namespace detail { |
43 | static void singleton_hs_init_weak(int* argc, char** argv[]) |
44 | __attribute__((__weakref__("hs_init" ))); |
45 | } // namespace detail |
46 | #endif |
47 | |
48 | SingletonVault::Type SingletonVault::defaultVaultType() { |
49 | #if FOLLY_SINGLETON_HAVE_DLSYM |
50 | bool isPython = dlsym(RTLD_DEFAULT, "Py_Main" ); |
51 | bool isHaskel = |
52 | detail::singleton_hs_init_weak || dlsym(RTLD_DEFAULT, "hs_init" ); |
53 | bool isJVM = dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs" ); |
54 | bool isD = dlsym(RTLD_DEFAULT, "_d_run_main" ); |
55 | |
56 | return isPython || isHaskel || isJVM || isD ? Type::Relaxed : Type::Strict; |
57 | #else |
58 | return Type::Relaxed; |
59 | #endif |
60 | } |
61 | |
62 | namespace detail { |
63 | |
64 | std::string TypeDescriptor::name() const { |
65 | auto ret = demangle(ti_.name()); |
66 | if (tag_ti_ != std::type_index(typeid(DefaultTag))) { |
67 | ret += "/" ; |
68 | ret += demangle(tag_ti_.name()); |
69 | } |
70 | return ret.toStdString(); |
71 | } |
72 | |
73 | // clang-format off |
74 | [[noreturn]] void singletonWarnDoubleRegistrationAndAbort( |
75 | const TypeDescriptor& type) { |
76 | // Ensure the availability of std::cerr |
77 | std::ios_base::Init ioInit; |
78 | std::cerr << "Double registration of singletons of the same " |
79 | "underlying type; check for multiple definitions " |
80 | "of type folly::Singleton<" |
81 | << type.name() << ">\n" ; |
82 | std::abort(); |
83 | } |
84 | |
85 | [[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort( |
86 | const TypeDescriptor& type) { |
87 | // Ensure the availability of std::cerr |
88 | std::ios_base::Init ioInit; |
89 | std::cerr << "Double registration of singletons of the same " |
90 | "underlying type; check for multiple definitions " |
91 | "of type folly::LeakySingleton<" |
92 | << type.name() << ">\n" ; |
93 | std::abort(); |
94 | } |
95 | |
96 | [[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort( |
97 | const TypeDescriptor& type) { |
98 | auto trace = detail::getSingletonStackTrace(); |
99 | LOG(FATAL) << "Creating instance for unregistered singleton: " << type.name() |
100 | << "\n" |
101 | << "Stacktrace:\n" << (trace != "" ? trace : "(not available)" ); |
102 | } |
103 | |
104 | [[noreturn]] void singletonWarnRegisterMockEarlyAndAbort( |
105 | const TypeDescriptor& type) { |
106 | LOG(FATAL) << "Registering mock before singleton was registered: " |
107 | << type.name(); |
108 | } |
109 | |
110 | void singletonWarnDestroyInstanceLeak( |
111 | const TypeDescriptor& type, |
112 | const void* ptr) { |
113 | LOG(ERROR) << "Singleton of type " << type.name() << " has a " |
114 | << "living reference at destroyInstances time; beware! Raw " |
115 | << "pointer is " << ptr << ". It is very likely " |
116 | << "that some other singleton is holding a shared_ptr to it. " |
117 | << "This singleton will be leaked (even if a shared_ptr to it " |
118 | << "is eventually released)." |
119 | << "Make sure dependencies between these singletons are " |
120 | << "properly defined." ; |
121 | } |
122 | |
123 | [[noreturn]] void singletonWarnCreateCircularDependencyAndAbort( |
124 | const TypeDescriptor& type) { |
125 | LOG(FATAL) << "circular singleton dependency: " << type.name(); |
126 | } |
127 | |
128 | [[noreturn]] void singletonWarnCreateUnregisteredAndAbort( |
129 | const TypeDescriptor& type) { |
130 | auto trace = detail::getSingletonStackTrace(); |
131 | LOG(FATAL) << "Creating instance for unregistered singleton: " << type.name() |
132 | << "\n" |
133 | << "Stacktrace:\n" << (trace != "" ? trace : "(not available)" ); |
134 | } |
135 | |
136 | [[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort( |
137 | const TypeDescriptor& type) { |
138 | auto trace = detail::getSingletonStackTrace(); |
139 | LOG(FATAL) << "Singleton " << type.name() << " requested before " |
140 | << "registrationComplete() call.\n" |
141 | << "This usually means that either main() never called " |
142 | << "folly::init, or singleton was requested before main() " |
143 | << "(which is not allowed).\n" |
144 | << "Stacktrace:\n" << (trace != "" ? trace : "(not available)" ); |
145 | } |
146 | |
147 | void singletonPrintDestructionStackTrace(const TypeDescriptor& type) { |
148 | auto trace = detail::getSingletonStackTrace(); |
149 | LOG(ERROR) << "Singleton " << type.name() << " was released.\n" |
150 | << "Stacktrace:\n" << (trace != "" ? trace : "(not available)" ); |
151 | } |
152 | |
153 | [[noreturn]] void singletonThrowNullCreator(const std::type_info& type) { |
154 | auto const msg = sformat( |
155 | "nullptr_t should be passed if you want {} to be default constructed" , |
156 | demangle(type)); |
157 | throw std::logic_error(msg); |
158 | } |
159 | |
160 | [[noreturn]] void singletonThrowGetInvokedAfterDestruction( |
161 | const TypeDescriptor& type) { |
162 | throw std::runtime_error( |
163 | "Raw pointer to a singleton requested after its destruction." |
164 | " Singleton type is: " + |
165 | type.name()); |
166 | } |
167 | // clang-format on |
168 | |
169 | } // namespace detail |
170 | |
171 | namespace { |
172 | |
173 | struct FatalHelper { |
174 | ~FatalHelper() { |
175 | if (!leakedSingletons_.empty()) { |
176 | std::string leakedTypes; |
177 | for (const auto& singleton : leakedSingletons_) { |
178 | leakedTypes += "\t" + singleton.name() + "\n" ; |
179 | } |
180 | LOG(DFATAL) << "Singletons of the following types had living references " |
181 | << "after destroyInstances was finished:\n" |
182 | << leakedTypes |
183 | << "beware! It is very likely that those singleton instances " |
184 | << "are leaked." ; |
185 | } |
186 | } |
187 | |
188 | std::vector<detail::TypeDescriptor> leakedSingletons_; |
189 | }; |
190 | |
191 | #if defined(__APPLE__) || defined(_MSC_VER) |
192 | // OS X doesn't support constructor priorities. |
193 | FatalHelper fatalHelper; |
194 | #else |
195 | FatalHelper __attribute__((__init_priority__(101))) fatalHelper; |
196 | #endif |
197 | |
198 | } // namespace |
199 | |
200 | SingletonVault::~SingletonVault() { |
201 | destroyInstances(); |
202 | } |
203 | |
204 | void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) { |
205 | auto state = state_.rlock(); |
206 | state->check(detail::SingletonVaultState::Type::Running); |
207 | |
208 | if (UNLIKELY(state->registrationComplete)) { |
209 | LOG(ERROR) << "Registering singleton after registrationComplete()." ; |
210 | } |
211 | |
212 | auto singletons = singletons_.wlock(); |
213 | CHECK_THROW( |
214 | singletons->emplace(entry->type(), entry).second, std::logic_error); |
215 | } |
216 | |
217 | void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) { |
218 | auto state = state_.rlock(); |
219 | state->check(detail::SingletonVaultState::Type::Running); |
220 | |
221 | if (UNLIKELY(state->registrationComplete)) { |
222 | LOG(ERROR) << "Registering for eager-load after registrationComplete()." ; |
223 | } |
224 | |
225 | CHECK_THROW(singletons_.rlock()->count(entry->type()), std::logic_error); |
226 | |
227 | auto eagerInitSingletons = eagerInitSingletons_.wlock(); |
228 | eagerInitSingletons->insert(entry); |
229 | } |
230 | |
231 | void SingletonVault::registrationComplete() { |
232 | std::atexit([]() { SingletonVault::singleton()->destroyInstances(); }); |
233 | |
234 | auto state = state_.wlock(); |
235 | state->check(detail::SingletonVaultState::Type::Running); |
236 | |
237 | if (state->registrationComplete) { |
238 | return; |
239 | } |
240 | |
241 | auto singletons = singletons_.rlock(); |
242 | if (type_ == Type::Strict) { |
243 | for (const auto& p : *singletons) { |
244 | if (p.second->hasLiveInstance()) { |
245 | throw std::runtime_error( |
246 | "Singleton " + p.first.name() + |
247 | " created before registration was complete." ); |
248 | } |
249 | } |
250 | } |
251 | |
252 | state->registrationComplete = true; |
253 | } |
254 | |
255 | void SingletonVault::doEagerInit() { |
256 | { |
257 | auto state = state_.rlock(); |
258 | state->check(detail::SingletonVaultState::Type::Running); |
259 | if (UNLIKELY(!state->registrationComplete)) { |
260 | throw std::logic_error("registrationComplete() not yet called" ); |
261 | } |
262 | } |
263 | |
264 | auto eagerInitSingletons = eagerInitSingletons_.rlock(); |
265 | for (auto* single : *eagerInitSingletons) { |
266 | single->createInstance(); |
267 | } |
268 | } |
269 | |
270 | void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) { |
271 | { |
272 | auto state = state_.rlock(); |
273 | state->check(detail::SingletonVaultState::Type::Running); |
274 | if (UNLIKELY(!state->registrationComplete)) { |
275 | throw std::logic_error("registrationComplete() not yet called" ); |
276 | } |
277 | } |
278 | |
279 | auto eagerInitSingletons = eagerInitSingletons_.rlock(); |
280 | auto countdown = |
281 | std::make_shared<std::atomic<size_t>>(eagerInitSingletons->size()); |
282 | for (auto* single : *eagerInitSingletons) { |
283 | // countdown is retained by shared_ptr, and will be alive until last lambda |
284 | // is done. notifyBaton is provided by the caller, and expected to remain |
285 | // present (if it's non-nullptr). singletonSet can go out of scope but |
286 | // its values, which are SingletonHolderBase pointers, are alive as long as |
287 | // SingletonVault is not being destroyed. |
288 | exe.add([=] { |
289 | // decrement counter and notify if requested, whether initialization |
290 | // was successful, was skipped (already initialized), or exception thrown. |
291 | SCOPE_EXIT { |
292 | if (--(*countdown) == 0) { |
293 | if (done != nullptr) { |
294 | done->post(); |
295 | } |
296 | } |
297 | }; |
298 | // if initialization is in progress in another thread, don't try to init |
299 | // here. Otherwise the current thread will block on 'createInstance'. |
300 | if (!single->creationStarted()) { |
301 | single->createInstance(); |
302 | } |
303 | }); |
304 | } |
305 | } |
306 | |
307 | void SingletonVault::destroyInstances() { |
308 | auto stateW = state_.wlock(); |
309 | if (stateW->state == detail::SingletonVaultState::Type::Quiescing) { |
310 | return; |
311 | } |
312 | stateW->state = detail::SingletonVaultState::Type::Quiescing; |
313 | |
314 | auto stateR = stateW.moveFromWriteToRead(); |
315 | { |
316 | auto singletons = singletons_.rlock(); |
317 | auto creationOrder = creationOrder_.rlock(); |
318 | |
319 | CHECK_GE(singletons->size(), creationOrder->size()); |
320 | |
321 | // Release all ReadMostlyMainPtrs at once |
322 | { |
323 | ReadMostlyMainPtrDeleter<> deleter; |
324 | for (auto& singleton_type : *creationOrder) { |
325 | singletons->at(singleton_type)->preDestroyInstance(deleter); |
326 | } |
327 | } |
328 | |
329 | for (auto type_iter = creationOrder->rbegin(); |
330 | type_iter != creationOrder->rend(); |
331 | ++type_iter) { |
332 | singletons->at(*type_iter)->destroyInstance(); |
333 | } |
334 | |
335 | for (auto& singleton_type : *creationOrder) { |
336 | auto instance = singletons->at(singleton_type); |
337 | if (!instance->hasLiveInstance()) { |
338 | continue; |
339 | } |
340 | |
341 | fatalHelper.leakedSingletons_.push_back(instance->type()); |
342 | } |
343 | } |
344 | |
345 | { |
346 | auto creationOrder = creationOrder_.wlock(); |
347 | creationOrder->clear(); |
348 | } |
349 | } |
350 | |
351 | void SingletonVault::reenableInstances() { |
352 | auto state = state_.wlock(); |
353 | |
354 | state->check(detail::SingletonVaultState::Type::Quiescing); |
355 | |
356 | state->state = detail::SingletonVaultState::Type::Running; |
357 | } |
358 | |
359 | void SingletonVault::scheduleDestroyInstances() { |
360 | // Add a dependency on folly::ThreadLocal to make sure all its static |
361 | // singletons are initalized first. |
362 | threadlocal_detail::StaticMeta<void, void>::instance(); |
363 | std::atexit([] { SingletonVault::singleton()->destroyInstances(); }); |
364 | } |
365 | |
366 | } // namespace folly |
367 | |