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