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