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