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