Singleton: un-inline initialization-time-only methods
[folly.git] / folly / Singleton.cpp
1 /*
2  * Copyright 2015 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 <string>
20
21 namespace folly {
22
23 namespace detail {
24
25 constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
26
27 }
28
29 namespace {
30
31 struct FatalHelper {
32   ~FatalHelper() {
33     if (!leakedSingletons_.empty()) {
34       std::string leakedTypes;
35       for (const auto& singleton : leakedSingletons_) {
36         leakedTypes += "\t" + singleton.name() + "\n";
37       }
38       LOG(DFATAL) << "Singletons of the following types had living references "
39                   << "after destroyInstances was finished:\n" << leakedTypes
40                   << "beware! It is very likely that those singleton instances "
41                   << "are leaked.";
42     }
43   }
44
45   std::vector<detail::TypeDescriptor> leakedSingletons_;
46 };
47
48 #if defined(__APPLE__) || defined(_MSC_VER)
49 // OS X doesn't support constructor priorities.
50 FatalHelper fatalHelper;
51 #else
52 FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
53 #endif
54
55 }
56
57 SingletonVault::~SingletonVault() { destroyInstances(); }
58
59 void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
60   RWSpinLock::ReadHolder rh(&stateMutex_);
61
62   stateCheck(SingletonVaultState::Running);
63
64   if (UNLIKELY(registrationComplete_)) {
65     throw std::logic_error(
66       "Registering singleton after registrationComplete().");
67   }
68
69   RWSpinLock::ReadHolder rhMutex(&mutex_);
70   CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
71               std::logic_error);
72
73   RWSpinLock::UpgradedHolder wh(&mutex_);
74   singletons_[entry->type()] = entry;
75 }
76
77 void SingletonVault::addEagerInitSingleton(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 for eager-load 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   eagerInitSingletons_.insert(entry);
93 }
94
95 void SingletonVault::registrationComplete() {
96   RequestContext::saveContext();
97   std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
98
99   RWSpinLock::WriteHolder wh(&stateMutex_);
100
101   stateCheck(SingletonVaultState::Running);
102
103   if (type_ == Type::Strict) {
104     for (const auto& p : singletons_) {
105       if (p.second->hasLiveInstance()) {
106         throw std::runtime_error(
107             "Singleton created before registration was complete.");
108       }
109     }
110   }
111
112   registrationComplete_ = true;
113 }
114
115 void SingletonVault::doEagerInit() {
116   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
117   {
118     RWSpinLock::ReadHolder rh(&stateMutex_);
119     stateCheck(SingletonVaultState::Running);
120     if (UNLIKELY(!registrationComplete_)) {
121       throw std::logic_error("registrationComplete() not yet called");
122     }
123     singletonSet = eagerInitSingletons_; // copy set of pointers
124   }
125
126   for (auto *single : singletonSet) {
127     single->createInstance();
128   }
129 }
130
131 Future<Unit> SingletonVault::doEagerInitVia(Executor* exe) {
132   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
133   {
134     RWSpinLock::ReadHolder rh(&stateMutex_);
135     stateCheck(SingletonVaultState::Running);
136     if (UNLIKELY(!registrationComplete_)) {
137       throw std::logic_error("registrationComplete() not yet called");
138     }
139     singletonSet = eagerInitSingletons_; // copy set of pointers
140   }
141
142   std::vector<Future<Unit>> resultFutures;
143   for (auto* single : singletonSet) {
144     resultFutures.emplace_back(via(exe).then([single] {
145       if (!single->creationStarted()) {
146         single->createInstance();
147       }
148     }));
149   }
150
151   return collectAll(resultFutures).via(exe).then();
152 }
153
154 void SingletonVault::destroyInstances() {
155   RWSpinLock::WriteHolder state_wh(&stateMutex_);
156
157   if (state_ == SingletonVaultState::Quiescing) {
158     return;
159   }
160   state_ = SingletonVaultState::Quiescing;
161
162   RWSpinLock::ReadHolder state_rh(std::move(state_wh));
163
164   {
165     RWSpinLock::ReadHolder rh(&mutex_);
166
167     CHECK_GE(singletons_.size(), creation_order_.size());
168
169     for (auto type_iter = creation_order_.rbegin();
170          type_iter != creation_order_.rend();
171          ++type_iter) {
172       singletons_[*type_iter]->destroyInstance();
173     }
174
175     for (auto& singleton_type: creation_order_) {
176       auto singleton = singletons_[singleton_type];
177       if (!singleton->hasLiveInstance()) {
178         continue;
179       }
180
181       fatalHelper.leakedSingletons_.push_back(singleton->type());
182     }
183   }
184
185   {
186     RWSpinLock::WriteHolder wh(&mutex_);
187     creation_order_.clear();
188   }
189 }
190
191 void SingletonVault::reenableInstances() {
192   RWSpinLock::WriteHolder state_wh(&stateMutex_);
193
194   stateCheck(SingletonVaultState::Quiescing);
195
196   state_ = SingletonVaultState::Running;
197 }
198
199 void SingletonVault::scheduleDestroyInstances() {
200   RequestContext::saveContext();
201
202   class SingletonVaultDestructor {
203    public:
204     ~SingletonVaultDestructor() {
205       SingletonVault::singleton()->destroyInstances();
206     }
207   };
208
209   // Here we intialize a singleton, which calls destroyInstances in its
210   // destructor. Because of singleton destruction order - it will be destroyed
211   // before all the singletons, which were initialized before it and after all
212   // the singletons initialized after it.
213   static SingletonVaultDestructor singletonVaultDestructor;
214 }
215
216 }