2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/Singleton.h>
25 #include <folly/FileUtil.h>
26 #include <folly/ScopeGuard.h>
32 [[noreturn]] void singletonWarnDoubleRegistrationAndAbort(
33 const TypeDescriptor& type) {
34 // Not using LOG(FATAL) or std::cerr because they may not be initialized yet.
36 o << "Double registration of singletons of the same "
37 << "underlying type; check for multiple definitions "
38 << "of type folly::Singleton<" << type.name() << ">" << std::endl;
40 writeFull(STDERR_FILENO, s.data(), s.size());
49 if (!leakedSingletons_.empty()) {
50 std::string leakedTypes;
51 for (const auto& singleton : leakedSingletons_) {
52 leakedTypes += "\t" + singleton.name() + "\n";
54 LOG(DFATAL) << "Singletons of the following types had living references "
55 << "after destroyInstances was finished:\n" << leakedTypes
56 << "beware! It is very likely that those singleton instances "
61 std::vector<detail::TypeDescriptor> leakedSingletons_;
64 #if defined(__APPLE__) || defined(_MSC_VER)
65 // OS X doesn't support constructor priorities.
66 FatalHelper fatalHelper;
68 FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
73 SingletonVault::~SingletonVault() { destroyInstances(); }
75 void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
76 RWSpinLock::ReadHolder rh(&stateMutex_);
78 stateCheck(SingletonVaultState::Running);
80 if (UNLIKELY(registrationComplete_)) {
81 LOG(ERROR) << "Registering singleton after registrationComplete().";
84 RWSpinLock::ReadHolder rhMutex(&mutex_);
85 CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
88 RWSpinLock::UpgradedHolder wh(&mutex_);
89 singletons_[entry->type()] = entry;
92 void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
93 RWSpinLock::ReadHolder rh(&stateMutex_);
95 stateCheck(SingletonVaultState::Running);
97 if (UNLIKELY(registrationComplete_)) {
98 LOG(ERROR) << "Registering for eager-load after registrationComplete().";
101 RWSpinLock::ReadHolder rhMutex(&mutex_);
102 CHECK_THROW(singletons_.find(entry->type()) != singletons_.end(),
105 RWSpinLock::UpgradedHolder wh(&mutex_);
106 eagerInitSingletons_.insert(entry);
109 void SingletonVault::registrationComplete() {
110 std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
112 RWSpinLock::WriteHolder wh(&stateMutex_);
114 stateCheck(SingletonVaultState::Running);
116 if (type_ == Type::Strict) {
117 for (const auto& p : singletons_) {
118 if (p.second->hasLiveInstance()) {
119 throw std::runtime_error(
120 "Singleton created before registration was complete.");
125 registrationComplete_ = true;
128 void SingletonVault::doEagerInit() {
129 std::unordered_set<detail::SingletonHolderBase*> singletonSet;
131 RWSpinLock::ReadHolder rh(&stateMutex_);
132 stateCheck(SingletonVaultState::Running);
133 if (UNLIKELY(!registrationComplete_)) {
134 throw std::logic_error("registrationComplete() not yet called");
136 singletonSet = eagerInitSingletons_; // copy set of pointers
139 for (auto *single : singletonSet) {
140 single->createInstance();
144 void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
145 std::unordered_set<detail::SingletonHolderBase*> singletonSet;
147 RWSpinLock::ReadHolder rh(&stateMutex_);
148 stateCheck(SingletonVaultState::Running);
149 if (UNLIKELY(!registrationComplete_)) {
150 throw std::logic_error("registrationComplete() not yet called");
152 singletonSet = eagerInitSingletons_; // copy set of pointers
155 auto countdown = std::make_shared<std::atomic<size_t>>(singletonSet.size());
156 for (auto* single : singletonSet) {
157 // countdown is retained by shared_ptr, and will be alive until last lambda
158 // is done. notifyBaton is provided by the caller, and expected to remain
159 // present (if it's non-nullptr). singletonSet can go out of scope but
160 // its values, which are SingletonHolderBase pointers, are alive as long as
161 // SingletonVault is not being destroyed.
163 // decrement counter and notify if requested, whether initialization
164 // was successful, was skipped (already initialized), or exception thrown.
166 if (--(*countdown) == 0) {
167 if (done != nullptr) {
172 // if initialization is in progress in another thread, don't try to init
173 // here. Otherwise the current thread will block on 'createInstance'.
174 if (!single->creationStarted()) {
175 single->createInstance();
181 void SingletonVault::destroyInstances() {
182 RWSpinLock::WriteHolder state_wh(&stateMutex_);
184 if (state_ == SingletonVaultState::Quiescing) {
187 state_ = SingletonVaultState::Quiescing;
189 RWSpinLock::ReadHolder state_rh(std::move(state_wh));
192 RWSpinLock::ReadHolder rh(&mutex_);
194 CHECK_GE(singletons_.size(), creation_order_.size());
196 // Release all ReadMostlyMainPtrs at once
198 ReadMostlyMainPtrDeleter<> deleter;
199 for (auto& singleton_type : creation_order_) {
200 singletons_[singleton_type]->preDestroyInstance(deleter);
204 for (auto type_iter = creation_order_.rbegin();
205 type_iter != creation_order_.rend();
207 singletons_[*type_iter]->destroyInstance();
210 for (auto& singleton_type: creation_order_) {
211 auto singleton = singletons_[singleton_type];
212 if (!singleton->hasLiveInstance()) {
216 fatalHelper.leakedSingletons_.push_back(singleton->type());
221 RWSpinLock::WriteHolder wh(&mutex_);
222 creation_order_.clear();
226 void SingletonVault::reenableInstances() {
227 RWSpinLock::WriteHolder state_wh(&stateMutex_);
229 stateCheck(SingletonVaultState::Quiescing);
231 state_ = SingletonVaultState::Running;
234 void SingletonVault::scheduleDestroyInstances() {
235 // Add a dependency on folly::ThreadLocal to make sure all its static
236 // singletons are initalized first.
237 threadlocal_detail::StaticMeta<void>::instance();
239 class SingletonVaultDestructor {
241 ~SingletonVaultDestructor() {
242 SingletonVault::singleton()->destroyInstances();
246 // Here we intialize a singleton, which calls destroyInstances in its
247 // destructor. Because of singleton destruction order - it will be destroyed
248 // before all the singletons, which were initialized before it and after all
249 // the singletons initialized after it.
250 static SingletonVaultDestructor singletonVaultDestructor;