Update documentation for Synchronized
[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     LOG(ERROR) << "Registering singleton after registrationComplete().";
82   }
83
84   RWSpinLock::ReadHolder rhMutex(&mutex_);
85   CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
86               std::logic_error);
87
88   RWSpinLock::UpgradedHolder wh(&mutex_);
89   singletons_[entry->type()] = entry;
90 }
91
92 void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
93   RWSpinLock::ReadHolder rh(&stateMutex_);
94
95   stateCheck(SingletonVaultState::Running);
96
97   if (UNLIKELY(registrationComplete_)) {
98     LOG(ERROR) << "Registering for eager-load after registrationComplete().";
99   }
100
101   RWSpinLock::ReadHolder rhMutex(&mutex_);
102   CHECK_THROW(singletons_.find(entry->type()) != singletons_.end(),
103               std::logic_error);
104
105   RWSpinLock::UpgradedHolder wh(&mutex_);
106   eagerInitSingletons_.insert(entry);
107 }
108
109 void SingletonVault::registrationComplete() {
110   std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
111
112   RWSpinLock::WriteHolder wh(&stateMutex_);
113
114   stateCheck(SingletonVaultState::Running);
115
116   if (type_ == Type::Strict) {
117     for (const auto& p : singletons_) {
118       if (p.second->hasLiveInstance()) {
119         throw std::runtime_error(
120             "Singleton created before registration was complete.");
121       }
122     }
123   }
124
125   registrationComplete_ = true;
126 }
127
128 void SingletonVault::doEagerInit() {
129   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
130   {
131     RWSpinLock::ReadHolder rh(&stateMutex_);
132     stateCheck(SingletonVaultState::Running);
133     if (UNLIKELY(!registrationComplete_)) {
134       throw std::logic_error("registrationComplete() not yet called");
135     }
136     singletonSet = eagerInitSingletons_; // copy set of pointers
137   }
138
139   for (auto *single : singletonSet) {
140     single->createInstance();
141   }
142 }
143
144 void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
145   std::unordered_set<detail::SingletonHolderBase*> singletonSet;
146   {
147     RWSpinLock::ReadHolder rh(&stateMutex_);
148     stateCheck(SingletonVaultState::Running);
149     if (UNLIKELY(!registrationComplete_)) {
150       throw std::logic_error("registrationComplete() not yet called");
151     }
152     singletonSet = eagerInitSingletons_; // copy set of pointers
153   }
154
155   auto countdown = std::make_shared<std::atomic<size_t>>(singletonSet.size());
156   for (auto* single : singletonSet) {
157     // countdown is retained by shared_ptr, and will be alive until last lambda
158     // is done.  notifyBaton is provided by the caller, and expected to remain
159     // present (if it's non-nullptr).  singletonSet can go out of scope but
160     // its values, which are SingletonHolderBase pointers, are alive as long as
161     // SingletonVault is not being destroyed.
162     exe.add([=] {
163       // decrement counter and notify if requested, whether initialization
164       // was successful, was skipped (already initialized), or exception thrown.
165       SCOPE_EXIT {
166         if (--(*countdown) == 0) {
167           if (done != nullptr) {
168             done->post();
169           }
170         }
171       };
172       // if initialization is in progress in another thread, don't try to init
173       // here.  Otherwise the current thread will block on 'createInstance'.
174       if (!single->creationStarted()) {
175         single->createInstance();
176       }
177     });
178   }
179 }
180
181 void SingletonVault::destroyInstances() {
182   RWSpinLock::WriteHolder state_wh(&stateMutex_);
183
184   if (state_ == SingletonVaultState::Quiescing) {
185     return;
186   }
187   state_ = SingletonVaultState::Quiescing;
188
189   RWSpinLock::ReadHolder state_rh(std::move(state_wh));
190
191   {
192     RWSpinLock::ReadHolder rh(&mutex_);
193
194     CHECK_GE(singletons_.size(), creation_order_.size());
195
196     for (auto type_iter = creation_order_.rbegin();
197          type_iter != creation_order_.rend();
198          ++type_iter) {
199       singletons_[*type_iter]->destroyInstance();
200     }
201
202     for (auto& singleton_type: creation_order_) {
203       auto singleton = singletons_[singleton_type];
204       if (!singleton->hasLiveInstance()) {
205         continue;
206       }
207
208       fatalHelper.leakedSingletons_.push_back(singleton->type());
209     }
210   }
211
212   {
213     RWSpinLock::WriteHolder wh(&mutex_);
214     creation_order_.clear();
215   }
216 }
217
218 void SingletonVault::reenableInstances() {
219   RWSpinLock::WriteHolder state_wh(&stateMutex_);
220
221   stateCheck(SingletonVaultState::Quiescing);
222
223   state_ = SingletonVaultState::Running;
224 }
225
226 void SingletonVault::scheduleDestroyInstances() {
227   // Add a dependency on folly::ThreadLocal to make sure all its static
228   // singletons are initalized first.
229   threadlocal_detail::StaticMeta<void>::instance();
230
231   class SingletonVaultDestructor {
232    public:
233     ~SingletonVaultDestructor() {
234       SingletonVault::singleton()->destroyInstances();
235     }
236   };
237
238   // Here we intialize a singleton, which calls destroyInstances in its
239   // destructor. Because of singleton destruction order - it will be destroyed
240   // before all the singletons, which were initialized before it and after all
241   // the singletons initialized after it.
242   static SingletonVaultDestructor singletonVaultDestructor;
243 }
244
245 }