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