Don't initialize kDestroyWaitTime in-class
[folly.git] / folly / Singleton.cpp
1 /*
2  * Copyright 2016 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 <sstream>
23 #include <string>
24
25 #include <folly/FileUtil.h>
26 #include <folly/ScopeGuard.h>
27
28 namespace folly {
29
30 namespace detail {
31
32 [[noreturn]] void singletonWarnDoubleRegistrationAndAbort(
33     const TypeDescriptor& type) {
34   // Not using LOG(FATAL) or std::cerr because they may not be initialized yet.
35   std::ostringstream o;
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;
39   auto s = o.str();
40   writeFull(STDERR_FILENO, s.data(), s.size());
41   std::abort();
42 }
43 }
44
45 namespace {
46
47 struct FatalHelper {
48   ~FatalHelper() {
49     if (!leakedSingletons_.empty()) {
50       std::string leakedTypes;
51       for (const auto& singleton : leakedSingletons_) {
52         leakedTypes += "\t" + singleton.name() + "\n";
53       }
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 "
57                   << "are leaked.";
58     }
59   }
60
61   std::vector<detail::TypeDescriptor> leakedSingletons_;
62 };
63
64 #if defined(__APPLE__) || defined(_MSC_VER)
65 // OS X doesn't support constructor priorities.
66 FatalHelper fatalHelper;
67 #else
68 FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
69 #endif
70
71 }
72
73 SingletonVault::~SingletonVault() { destroyInstances(); }
74
75 void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
76   RWSpinLock::ReadHolder rh(&stateMutex_);
77
78   stateCheck(SingletonVaultState::Running);
79
80   if (UNLIKELY(registrationComplete_)) {
81     throw std::logic_error(
82       "Registering singleton after registrationComplete().");
83   }
84
85   RWSpinLock::ReadHolder rhMutex(&mutex_);
86   CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
87               std::logic_error);
88
89   RWSpinLock::UpgradedHolder wh(&mutex_);
90   singletons_[entry->type()] = entry;
91 }
92
93 void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
94   RWSpinLock::ReadHolder rh(&stateMutex_);
95
96   stateCheck(SingletonVaultState::Running);
97
98   if (UNLIKELY(registrationComplete_)) {
99     throw std::logic_error(
100         "Registering for eager-load after registrationComplete().");
101   }
102
103   RWSpinLock::ReadHolder rhMutex(&mutex_);
104   CHECK_THROW(singletons_.find(entry->type()) != singletons_.end(),
105               std::logic_error);
106
107   RWSpinLock::UpgradedHolder wh(&mutex_);
108   eagerInitSingletons_.insert(entry);
109 }
110
111 void SingletonVault::registrationComplete() {
112   std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
113
114   RWSpinLock::WriteHolder wh(&stateMutex_);
115
116   stateCheck(SingletonVaultState::Running);
117
118   if (type_ == Type::Strict) {
119     for (const auto& p : singletons_) {
120       if (p.second->hasLiveInstance()) {
121         throw std::runtime_error(
122             "Singleton created before registration was complete.");
123       }
124     }
125   }
126
127   registrationComplete_ = true;
128 }
129
130 void SingletonVault::doEagerInit() {
131   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
132   {
133     RWSpinLock::ReadHolder rh(&stateMutex_);
134     stateCheck(SingletonVaultState::Running);
135     if (UNLIKELY(!registrationComplete_)) {
136       throw std::logic_error("registrationComplete() not yet called");
137     }
138     singletonSet = eagerInitSingletons_; // copy set of pointers
139   }
140
141   for (auto *single : singletonSet) {
142     single->createInstance();
143   }
144 }
145
146 void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
147   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
148   {
149     RWSpinLock::ReadHolder rh(&stateMutex_);
150     stateCheck(SingletonVaultState::Running);
151     if (UNLIKELY(!registrationComplete_)) {
152       throw std::logic_error("registrationComplete() not yet called");
153     }
154     singletonSet = eagerInitSingletons_; // copy set of pointers
155   }
156
157   auto countdown = std::make_shared<std::atomic<size_t>>(singletonSet.size());
158   for (auto* single : singletonSet) {
159     // countdown is retained by shared_ptr, and will be alive until last lambda
160     // is done.  notifyBaton is provided by the caller, and expected to remain
161     // present (if it's non-nullptr).  singletonSet can go out of scope but
162     // its values, which are SingletonHolderBase pointers, are alive as long as
163     // SingletonVault is not being destroyed.
164     exe.add([=] {
165       // decrement counter and notify if requested, whether initialization
166       // was successful, was skipped (already initialized), or exception thrown.
167       SCOPE_EXIT {
168         if (--(*countdown) == 0) {
169           if (done != nullptr) {
170             done->post();
171           }
172         }
173       };
174       // if initialization is in progress in another thread, don't try to init
175       // here.  Otherwise the current thread will block on 'createInstance'.
176       if (!single->creationStarted()) {
177         single->createInstance();
178       }
179     });
180   }
181 }
182
183 void SingletonVault::destroyInstances() {
184   RWSpinLock::WriteHolder state_wh(&stateMutex_);
185
186   if (state_ == SingletonVaultState::Quiescing) {
187     return;
188   }
189   state_ = SingletonVaultState::Quiescing;
190
191   RWSpinLock::ReadHolder state_rh(std::move(state_wh));
192
193   {
194     RWSpinLock::ReadHolder rh(&mutex_);
195
196     CHECK_GE(singletons_.size(), creation_order_.size());
197
198     for (auto type_iter = creation_order_.rbegin();
199          type_iter != creation_order_.rend();
200          ++type_iter) {
201       singletons_[*type_iter]->destroyInstance();
202     }
203
204     for (auto& singleton_type: creation_order_) {
205       auto singleton = singletons_[singleton_type];
206       if (!singleton->hasLiveInstance()) {
207         continue;
208       }
209
210       fatalHelper.leakedSingletons_.push_back(singleton->type());
211     }
212   }
213
214   {
215     RWSpinLock::WriteHolder wh(&mutex_);
216     creation_order_.clear();
217   }
218 }
219
220 void SingletonVault::reenableInstances() {
221   RWSpinLock::WriteHolder state_wh(&stateMutex_);
222
223   stateCheck(SingletonVaultState::Quiescing);
224
225   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>::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 }