2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include "FiberManagerMap.h"
19 #include <unordered_map>
21 #include <folly/ThreadLocal.h>
22 #include <folly/Synchronized.h>
24 namespace folly { namespace fibers {
28 class EventBaseOnDestructionCallback : public EventBase::LoopCallback {
30 explicit EventBaseOnDestructionCallback(EventBase& evb) : evb_(evb) {}
31 void runLoopCallback() noexcept override;
39 static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
40 return instance().getImpl(evb, opts);
43 static std::unique_ptr<FiberManager> erase(EventBase& evb) {
44 return instance().eraseImpl(evb);
50 // Leak this intentionally. During shutdown, we may call getFiberManager,
51 // and want access to the fiber managers during that time.
52 static GlobalCache& instance() {
53 static auto ret = new GlobalCache();
57 FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
58 std::lock_guard<std::mutex> lg(mutex_);
60 auto& fmPtrRef = map_[&evb];
63 auto loopController = make_unique<EventBaseLoopController>();
64 loopController->attachEventBase(evb);
65 evb.runOnDestruction(new EventBaseOnDestructionCallback(evb));
67 fmPtrRef = make_unique<FiberManager>(std::move(loopController), opts);
73 std::unique_ptr<FiberManager> eraseImpl(EventBase& evb) {
74 std::lock_guard<std::mutex> lg(mutex_);
76 DCHECK_EQ(1, map_.count(&evb));
78 auto ret = std::move(map_[&evb]);
84 std::unordered_map<EventBase*, std::unique_ptr<FiberManager>> map_;
87 constexpr size_t kEraseListMaxSize = 64;
89 class ThreadLocalCache {
91 static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
92 return instance()->getImpl(evb, opts);
95 static void erase(EventBase& evb) {
96 for (auto& localInstance : instance().accessAllThreads()) {
97 SYNCHRONIZED(info, localInstance.eraseInfo_) {
98 if (info.eraseList.size() >= kEraseListMaxSize) {
101 info.eraseList.push_back(&evb);
103 localInstance.eraseRequested_ = true;
109 ThreadLocalCache() {}
111 struct ThreadLocalCacheTag {};
112 using ThreadThreadLocalCache = ThreadLocal<ThreadLocalCache, ThreadLocalCacheTag>;
114 // Leak this intentionally. During shutdown, we may call getFiberManager,
115 // and want access to the fiber managers during that time.
116 static ThreadThreadLocalCache& instance() {
117 static auto ret = new ThreadThreadLocalCache([]() { return new ThreadLocalCache(); });
121 FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
124 auto& fmPtrRef = map_[&evb];
126 fmPtrRef = &GlobalCache::get(evb, opts);
129 DCHECK(fmPtrRef != nullptr);
135 if (!eraseRequested_.load()) {
139 SYNCHRONIZED(info, eraseInfo_) {
143 for (auto evbPtr : info.eraseList) {
148 info.eraseList.clear();
149 info.eraseAll = false;
150 eraseRequested_ = false;
154 std::unordered_map<EventBase*, FiberManager*> map_;
155 std::atomic<bool> eraseRequested_{false};
158 bool eraseAll{false};
159 std::vector<EventBase*> eraseList;
162 folly::Synchronized<EraseInfo> eraseInfo_;
165 void EventBaseOnDestructionCallback::runLoopCallback() noexcept {
166 auto fm = GlobalCache::erase(evb_);
167 DCHECK(fm.get() != nullptr);
168 ThreadLocalCache::erase(evb_);
170 while (fm->hasTasks()) {
171 fm->loopUntilNoReady();
180 FiberManager& getFiberManager(EventBase& evb,
181 const FiberManager::Options& opts) {
182 return ThreadLocalCache::get(evb, opts);