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
39namespace folly {
40
41#if FOLLY_SINGLETON_HAVE_DLSYM
42namespace detail {
43static void singleton_hs_init_weak(int* argc, char** argv[])
44 __attribute__((__weakref__("hs_init")));
45} // namespace detail
46#endif
47
48SingletonVault::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
62namespace detail {
63
64std::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
110void 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
147void 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
171namespace {
172
173struct 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.
193FatalHelper fatalHelper;
194#else
195FatalHelper __attribute__((__init_priority__(101))) fatalHelper;
196#endif
197
198} // namespace
199
200SingletonVault::~SingletonVault() {
201 destroyInstances();
202}
203
204void 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
217void 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
231void 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
255void 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
270void 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
307void 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
351void 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
359void 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