Namespace portability symbols better in Singleton.cpp
[folly.git] / folly / Singleton.cpp
1 /*
2  * Copyright 2017 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
19 #ifndef _WIN32
20 #include <dlfcn.h>
21 #endif
22
23 #include <atomic>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <iostream>
27 #include <string>
28
29 #include <folly/ScopeGuard.h>
30
31 #if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__)
32 #define FOLLY_SINGLETON_HAVE_DLSYM 1
33 #endif
34
35 namespace folly {
36
37 #if FOLLY_SINGLETON_HAVE_DLSYM
38 namespace detail {
39 static void singleton_hs_init_weak(int* argc, char** argv[])
40     __attribute__((__weakref__("hs_init")));
41 } // namespace detail
42 #endif
43
44 SingletonVault::Type SingletonVault::defaultVaultType() {
45 #if FOLLY_SINGLETON_HAVE_DLSYM
46   bool isPython = dlsym(RTLD_DEFAULT, "Py_Main");
47   bool isHaskel =
48       detail::singleton_hs_init_weak || dlsym(RTLD_DEFAULT, "hs_init");
49   bool isJVM = dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs");
50   bool isD = dlsym(RTLD_DEFAULT, "_d_run_main");
51
52   return isPython || isHaskel || isJVM || isD ? Type::Relaxed : Type::Strict;
53 #else
54   return Type::Relaxed;
55 #endif
56 }
57
58 namespace detail {
59
60 [[noreturn]] void singletonWarnDoubleRegistrationAndAbort(
61     const TypeDescriptor& type) {
62   // Ensure the availability of std::cerr
63   std::ios_base::Init ioInit;
64   std::cerr << "Double registration of singletons of the same "
65                "underlying type; check for multiple definitions "
66                "of type folly::Singleton<"
67             << type.name() << ">\n";
68   std::abort();
69 }
70 } // namespace detail
71
72 namespace {
73
74 struct FatalHelper {
75   ~FatalHelper() {
76     if (!leakedSingletons_.empty()) {
77       std::string leakedTypes;
78       for (const auto& singleton : leakedSingletons_) {
79         leakedTypes += "\t" + singleton.name() + "\n";
80       }
81       LOG(DFATAL) << "Singletons of the following types had living references "
82                   << "after destroyInstances was finished:\n" << leakedTypes
83                   << "beware! It is very likely that those singleton instances "
84                   << "are leaked.";
85     }
86   }
87
88   std::vector<detail::TypeDescriptor> leakedSingletons_;
89 };
90
91 #if defined(__APPLE__) || defined(_MSC_VER)
92 // OS X doesn't support constructor priorities.
93 FatalHelper fatalHelper;
94 #else
95 FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
96 #endif
97
98 } // namespace
99
100 SingletonVault::~SingletonVault() { destroyInstances(); }
101
102 void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
103   auto state = state_.rlock();
104   stateCheck(SingletonVaultState::Running, *state);
105
106   if (UNLIKELY(state->registrationComplete)) {
107     LOG(ERROR) << "Registering singleton after registrationComplete().";
108   }
109
110   auto singletons = singletons_.wlock();
111   CHECK_THROW(
112       singletons->emplace(entry->type(), entry).second, std::logic_error);
113 }
114
115 void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
116   auto state = state_.rlock();
117   stateCheck(SingletonVaultState::Running, *state);
118
119   if (UNLIKELY(state->registrationComplete)) {
120     LOG(ERROR) << "Registering for eager-load after registrationComplete().";
121   }
122
123   CHECK_THROW(singletons_.rlock()->count(entry->type()), std::logic_error);
124
125   auto eagerInitSingletons = eagerInitSingletons_.wlock();
126   eagerInitSingletons->insert(entry);
127 }
128
129 void SingletonVault::registrationComplete() {
130   std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
131
132   auto state = state_.wlock();
133   stateCheck(SingletonVaultState::Running, *state);
134
135   if (state->registrationComplete) {
136     return;
137   }
138
139   auto singletons = singletons_.rlock();
140   if (type_ == Type::Strict) {
141     for (const auto& p : *singletons) {
142       if (p.second->hasLiveInstance()) {
143         throw std::runtime_error(
144             "Singleton " + p.first.name() +
145             " created before registration was complete.");
146       }
147     }
148   }
149
150   state->registrationComplete = true;
151 }
152
153 void SingletonVault::doEagerInit() {
154   {
155     auto state = state_.rlock();
156     stateCheck(SingletonVaultState::Running, *state);
157     if (UNLIKELY(!state->registrationComplete)) {
158       throw std::logic_error("registrationComplete() not yet called");
159     }
160   }
161
162   auto eagerInitSingletons = eagerInitSingletons_.rlock();
163   for (auto* single : *eagerInitSingletons) {
164     single->createInstance();
165   }
166 }
167
168 void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
169   {
170     auto state = state_.rlock();
171     stateCheck(SingletonVaultState::Running, *state);
172     if (UNLIKELY(!state->registrationComplete)) {
173       throw std::logic_error("registrationComplete() not yet called");
174     }
175   }
176
177   auto eagerInitSingletons = eagerInitSingletons_.rlock();
178   auto countdown =
179       std::make_shared<std::atomic<size_t>>(eagerInitSingletons->size());
180   for (auto* single : *eagerInitSingletons) {
181     // countdown is retained by shared_ptr, and will be alive until last lambda
182     // is done.  notifyBaton is provided by the caller, and expected to remain
183     // present (if it's non-nullptr).  singletonSet can go out of scope but
184     // its values, which are SingletonHolderBase pointers, are alive as long as
185     // SingletonVault is not being destroyed.
186     exe.add([=] {
187       // decrement counter and notify if requested, whether initialization
188       // was successful, was skipped (already initialized), or exception thrown.
189       SCOPE_EXIT {
190         if (--(*countdown) == 0) {
191           if (done != nullptr) {
192             done->post();
193           }
194         }
195       };
196       // if initialization is in progress in another thread, don't try to init
197       // here.  Otherwise the current thread will block on 'createInstance'.
198       if (!single->creationStarted()) {
199         single->createInstance();
200       }
201     });
202   }
203 }
204
205 void SingletonVault::destroyInstances() {
206   auto stateW = state_.wlock();
207   if (stateW->state == SingletonVaultState::Quiescing) {
208     return;
209   }
210   stateW->state = SingletonVaultState::Quiescing;
211
212   auto stateR = stateW.moveFromWriteToRead();
213   {
214     auto singletons = singletons_.rlock();
215     auto creationOrder = creationOrder_.rlock();
216
217     CHECK_GE(singletons->size(), creationOrder->size());
218
219     // Release all ReadMostlyMainPtrs at once
220     {
221       ReadMostlyMainPtrDeleter<> deleter;
222       for (auto& singleton_type : *creationOrder) {
223         singletons->at(singleton_type)->preDestroyInstance(deleter);
224       }
225     }
226
227     for (auto type_iter = creationOrder->rbegin();
228          type_iter != creationOrder->rend();
229          ++type_iter) {
230       singletons->at(*type_iter)->destroyInstance();
231     }
232
233     for (auto& singleton_type : *creationOrder) {
234       auto singleton = singletons->at(singleton_type);
235       if (!singleton->hasLiveInstance()) {
236         continue;
237       }
238
239       fatalHelper.leakedSingletons_.push_back(singleton->type());
240     }
241   }
242
243   {
244     auto creationOrder = creationOrder_.wlock();
245     creationOrder->clear();
246   }
247 }
248
249 void SingletonVault::reenableInstances() {
250   auto state = state_.wlock();
251
252   stateCheck(SingletonVaultState::Quiescing, *state);
253
254   state->state = SingletonVaultState::Running;
255 }
256
257 void SingletonVault::scheduleDestroyInstances() {
258   // Add a dependency on folly::ThreadLocal to make sure all its static
259   // singletons are initalized first.
260   threadlocal_detail::StaticMeta<void, void>::instance();
261
262   class SingletonVaultDestructor {
263    public:
264     ~SingletonVaultDestructor() {
265       SingletonVault::singleton()->destroyInstances();
266     }
267   };
268
269   // Here we intialize a singleton, which calls destroyInstances in its
270   // destructor. Because of singleton destruction order - it will be destroyed
271   // before all the singletons, which were initialized before it and after all
272   // the singletons initialized after it.
273   static SingletonVaultDestructor singletonVaultDestructor;
274 }
275
276 } // namespace folly