Fix folly::Singleton to work in dynamically linked binaries
[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 <string>
21
22 #include <folly/ScopeGuard.h>
23
24 namespace folly {
25
26 namespace detail {
27
28 // This implementation should always live in .cpp file.
29 StaticSingletonManager& StaticSingletonManager::instance() {
30   static StaticSingletonManager* instance = new StaticSingletonManager();
31   return *instance;
32 }
33
34 constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
35
36 }
37
38 namespace {
39
40 struct FatalHelper {
41   ~FatalHelper() {
42     if (!leakedSingletons_.empty()) {
43       std::string leakedTypes;
44       for (const auto& singleton : leakedSingletons_) {
45         leakedTypes += "\t" + singleton.name() + "\n";
46       }
47       LOG(DFATAL) << "Singletons of the following types had living references "
48                   << "after destroyInstances was finished:\n" << leakedTypes
49                   << "beware! It is very likely that those singleton instances "
50                   << "are leaked.";
51     }
52   }
53
54   std::vector<detail::TypeDescriptor> leakedSingletons_;
55 };
56
57 #if defined(__APPLE__) || defined(_MSC_VER)
58 // OS X doesn't support constructor priorities.
59 FatalHelper fatalHelper;
60 #else
61 FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
62 #endif
63
64 }
65
66 SingletonVault::~SingletonVault() { destroyInstances(); }
67
68 void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
69   RWSpinLock::ReadHolder rh(&stateMutex_);
70
71   stateCheck(SingletonVaultState::Running);
72
73   if (UNLIKELY(registrationComplete_)) {
74     throw std::logic_error(
75       "Registering singleton after registrationComplete().");
76   }
77
78   RWSpinLock::ReadHolder rhMutex(&mutex_);
79   CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
80               std::logic_error);
81
82   RWSpinLock::UpgradedHolder wh(&mutex_);
83   singletons_[entry->type()] = entry;
84 }
85
86 void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
87   RWSpinLock::ReadHolder rh(&stateMutex_);
88
89   stateCheck(SingletonVaultState::Running);
90
91   if (UNLIKELY(registrationComplete_)) {
92     throw std::logic_error(
93         "Registering for eager-load after registrationComplete().");
94   }
95
96   RWSpinLock::ReadHolder rhMutex(&mutex_);
97   CHECK_THROW(singletons_.find(entry->type()) != singletons_.end(),
98               std::logic_error);
99
100   RWSpinLock::UpgradedHolder wh(&mutex_);
101   eagerInitSingletons_.insert(entry);
102 }
103
104 void SingletonVault::registrationComplete() {
105   std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
106
107   RWSpinLock::WriteHolder wh(&stateMutex_);
108
109   stateCheck(SingletonVaultState::Running);
110
111   if (type_ == Type::Strict) {
112     for (const auto& p : singletons_) {
113       if (p.second->hasLiveInstance()) {
114         throw std::runtime_error(
115             "Singleton created before registration was complete.");
116       }
117     }
118   }
119
120   registrationComplete_ = true;
121 }
122
123 void SingletonVault::doEagerInit() {
124   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
125   {
126     RWSpinLock::ReadHolder rh(&stateMutex_);
127     stateCheck(SingletonVaultState::Running);
128     if (UNLIKELY(!registrationComplete_)) {
129       throw std::logic_error("registrationComplete() not yet called");
130     }
131     singletonSet = eagerInitSingletons_; // copy set of pointers
132   }
133
134   for (auto *single : singletonSet) {
135     single->createInstance();
136   }
137 }
138
139 void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
140   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
141   {
142     RWSpinLock::ReadHolder rh(&stateMutex_);
143     stateCheck(SingletonVaultState::Running);
144     if (UNLIKELY(!registrationComplete_)) {
145       throw std::logic_error("registrationComplete() not yet called");
146     }
147     singletonSet = eagerInitSingletons_; // copy set of pointers
148   }
149
150   auto countdown = std::make_shared<std::atomic<size_t>>(singletonSet.size());
151   for (auto* single : singletonSet) {
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   RWSpinLock::WriteHolder state_wh(&stateMutex_);
178
179   if (state_ == SingletonVaultState::Quiescing) {
180     return;
181   }
182   state_ = SingletonVaultState::Quiescing;
183
184   RWSpinLock::ReadHolder state_rh(std::move(state_wh));
185
186   {
187     RWSpinLock::ReadHolder rh(&mutex_);
188
189     CHECK_GE(singletons_.size(), creation_order_.size());
190
191     for (auto type_iter = creation_order_.rbegin();
192          type_iter != creation_order_.rend();
193          ++type_iter) {
194       singletons_[*type_iter]->destroyInstance();
195     }
196
197     for (auto& singleton_type: creation_order_) {
198       auto singleton = singletons_[singleton_type];
199       if (!singleton->hasLiveInstance()) {
200         continue;
201       }
202
203       fatalHelper.leakedSingletons_.push_back(singleton->type());
204     }
205   }
206
207   {
208     RWSpinLock::WriteHolder wh(&mutex_);
209     creation_order_.clear();
210   }
211 }
212
213 void SingletonVault::reenableInstances() {
214   RWSpinLock::WriteHolder state_wh(&stateMutex_);
215
216   stateCheck(SingletonVaultState::Quiescing);
217
218   state_ = SingletonVaultState::Running;
219 }
220
221 void SingletonVault::scheduleDestroyInstances() {
222   // Add a dependency on folly::ThreadLocal to make sure all its static
223   // singletons are initalized first.
224   threadlocal_detail::StaticMeta<void>::instance();
225
226   class SingletonVaultDestructor {
227    public:
228     ~SingletonVaultDestructor() {
229       SingletonVault::singleton()->destroyInstances();
230     }
231   };
232
233   // Here we intialize a singleton, which calls destroyInstances in its
234   // destructor. Because of singleton destruction order - it will be destroyed
235   // before all the singletons, which were initialized before it and after all
236   // the singletons initialized after it.
237   static SingletonVaultDestructor singletonVaultDestructor;
238 }
239
240 }