2 * Copyright 2015 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>
22 #include <folly/ScopeGuard.h>
28 constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
36 if (!leakedSingletons_.empty()) {
37 std::string leakedTypes;
38 for (const auto& singleton : leakedSingletons_) {
39 leakedTypes += "\t" + singleton.name() + "\n";
41 LOG(DFATAL) << "Singletons of the following types had living references "
42 << "after destroyInstances was finished:\n" << leakedTypes
43 << "beware! It is very likely that those singleton instances "
48 std::vector<detail::TypeDescriptor> leakedSingletons_;
51 #if defined(__APPLE__) || defined(_MSC_VER)
52 // OS X doesn't support constructor priorities.
53 FatalHelper fatalHelper;
55 FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
60 SingletonVault::~SingletonVault() { destroyInstances(); }
62 void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
63 RWSpinLock::ReadHolder rh(&stateMutex_);
65 stateCheck(SingletonVaultState::Running);
67 if (UNLIKELY(registrationComplete_)) {
68 throw std::logic_error(
69 "Registering singleton after registrationComplete().");
72 RWSpinLock::ReadHolder rhMutex(&mutex_);
73 CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
76 RWSpinLock::UpgradedHolder wh(&mutex_);
77 singletons_[entry->type()] = entry;
80 void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
81 RWSpinLock::ReadHolder rh(&stateMutex_);
83 stateCheck(SingletonVaultState::Running);
85 if (UNLIKELY(registrationComplete_)) {
86 throw std::logic_error(
87 "Registering for eager-load after registrationComplete().");
90 RWSpinLock::ReadHolder rhMutex(&mutex_);
91 CHECK_THROW(singletons_.find(entry->type()) != singletons_.end(),
94 RWSpinLock::UpgradedHolder wh(&mutex_);
95 eagerInitSingletons_.insert(entry);
98 void SingletonVault::registrationComplete() {
99 RequestContext::saveContext();
100 std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
102 RWSpinLock::WriteHolder wh(&stateMutex_);
104 stateCheck(SingletonVaultState::Running);
106 if (type_ == Type::Strict) {
107 for (const auto& p : singletons_) {
108 if (p.second->hasLiveInstance()) {
109 throw std::runtime_error(
110 "Singleton created before registration was complete.");
115 registrationComplete_ = true;
118 void SingletonVault::doEagerInit() {
119 std::unordered_set<detail::SingletonHolderBase*> singletonSet;
121 RWSpinLock::ReadHolder rh(&stateMutex_);
122 stateCheck(SingletonVaultState::Running);
123 if (UNLIKELY(!registrationComplete_)) {
124 throw std::logic_error("registrationComplete() not yet called");
126 singletonSet = eagerInitSingletons_; // copy set of pointers
129 for (auto *single : singletonSet) {
130 single->createInstance();
134 void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
135 std::unordered_set<detail::SingletonHolderBase*> singletonSet;
137 RWSpinLock::ReadHolder rh(&stateMutex_);
138 stateCheck(SingletonVaultState::Running);
139 if (UNLIKELY(!registrationComplete_)) {
140 throw std::logic_error("registrationComplete() not yet called");
142 singletonSet = eagerInitSingletons_; // copy set of pointers
145 auto countdown = std::make_shared<std::atomic<size_t>>(singletonSet.size());
146 for (auto* single : singletonSet) {
147 // countdown is retained by shared_ptr, and will be alive until last lambda
148 // is done. notifyBaton is provided by the caller, and expected to remain
149 // present (if it's non-nullptr). singletonSet can go out of scope but
150 // its values, which are SingletonHolderBase pointers, are alive as long as
151 // SingletonVault is not being destroyed.
153 // decrement counter and notify if requested, whether initialization
154 // was successful, was skipped (already initialized), or exception thrown.
156 if (--(*countdown) == 0) {
157 if (done != nullptr) {
162 // if initialization is in progress in another thread, don't try to init
163 // here. Otherwise the current thread will block on 'createInstance'.
164 if (!single->creationStarted()) {
165 single->createInstance();
171 void SingletonVault::destroyInstances() {
172 RWSpinLock::WriteHolder state_wh(&stateMutex_);
174 if (state_ == SingletonVaultState::Quiescing) {
177 state_ = SingletonVaultState::Quiescing;
179 RWSpinLock::ReadHolder state_rh(std::move(state_wh));
182 RWSpinLock::ReadHolder rh(&mutex_);
184 CHECK_GE(singletons_.size(), creation_order_.size());
186 for (auto type_iter = creation_order_.rbegin();
187 type_iter != creation_order_.rend();
189 singletons_[*type_iter]->destroyInstance();
192 for (auto& singleton_type: creation_order_) {
193 auto singleton = singletons_[singleton_type];
194 if (!singleton->hasLiveInstance()) {
198 fatalHelper.leakedSingletons_.push_back(singleton->type());
203 RWSpinLock::WriteHolder wh(&mutex_);
204 creation_order_.clear();
208 void SingletonVault::reenableInstances() {
209 RWSpinLock::WriteHolder state_wh(&stateMutex_);
211 stateCheck(SingletonVaultState::Quiescing);
213 state_ = SingletonVaultState::Running;
216 void SingletonVault::scheduleDestroyInstances() {
217 RequestContext::saveContext();
219 class SingletonVaultDestructor {
221 ~SingletonVaultDestructor() {
222 SingletonVault::singleton()->destroyInstances();
226 // Here we intialize a singleton, which calls destroyInstances in its
227 // destructor. Because of singleton destruction order - it will be destroyed
228 // before all the singletons, which were initialized before it and after all
229 // the singletons initialized after it.
230 static SingletonVaultDestructor singletonVaultDestructor;