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/Synchronized.h>
22 #include <folly/ThreadLocal.h>
29 class EventBaseOnDestructionCallback : public EventBase::LoopCallback {
31 explicit EventBaseOnDestructionCallback(EventBase& evb) : evb_(evb) {}
32 void runLoopCallback() noexcept override;
40 static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
41 return instance().getImpl(evb, opts);
44 static std::unique_ptr<FiberManager> erase(EventBase& evb) {
45 return instance().eraseImpl(evb);
51 // Leak this intentionally. During shutdown, we may call getFiberManager,
52 // and want access to the fiber managers during that time.
53 static GlobalCache& instance() {
54 static auto ret = new GlobalCache();
58 FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
59 std::lock_guard<std::mutex> lg(mutex_);
61 auto& fmPtrRef = map_[&evb];
64 auto loopController = make_unique<EventBaseLoopController>();
65 loopController->attachEventBase(evb);
66 evb.runOnDestruction(new EventBaseOnDestructionCallback(evb));
68 fmPtrRef = make_unique<FiberManager>(std::move(loopController), opts);
74 std::unique_ptr<FiberManager> eraseImpl(EventBase& evb) {
75 std::lock_guard<std::mutex> lg(mutex_);
77 DCHECK_EQ(1, map_.count(&evb));
79 auto ret = std::move(map_[&evb]);
85 std::unordered_map<EventBase*, std::unique_ptr<FiberManager>> map_;
88 constexpr size_t kEraseListMaxSize = 64;
90 class ThreadLocalCache {
92 static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
93 return instance()->getImpl(evb, opts);
96 static void erase(EventBase& evb) {
97 for (auto& localInstance : instance().accessAllThreads()) {
98 SYNCHRONIZED(info, localInstance.eraseInfo_) {
99 if (info.eraseList.size() >= kEraseListMaxSize) {
100 info.eraseAll = true;
102 info.eraseList.push_back(&evb);
104 localInstance.eraseRequested_ = true;
110 ThreadLocalCache() {}
112 struct ThreadLocalCacheTag {};
113 using ThreadThreadLocalCache =
114 ThreadLocal<ThreadLocalCache, ThreadLocalCacheTag>;
116 // Leak this intentionally. During shutdown, we may call getFiberManager,
117 // and want access to the fiber managers during that time.
118 static ThreadThreadLocalCache& instance() {
120 new ThreadThreadLocalCache([]() { return new ThreadLocalCache(); });
124 FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
127 auto& fmPtrRef = map_[&evb];
129 fmPtrRef = &GlobalCache::get(evb, opts);
132 DCHECK(fmPtrRef != nullptr);
138 if (!eraseRequested_.load()) {
142 SYNCHRONIZED(info, eraseInfo_) {
146 for (auto evbPtr : info.eraseList) {
151 info.eraseList.clear();
152 info.eraseAll = false;
153 eraseRequested_ = false;
157 std::unordered_map<EventBase*, FiberManager*> map_;
158 std::atomic<bool> eraseRequested_{false};
161 bool eraseAll{false};
162 std::vector<EventBase*> eraseList;
165 folly::Synchronized<EraseInfo> eraseInfo_;
168 void EventBaseOnDestructionCallback::runLoopCallback() noexcept {
169 auto fm = GlobalCache::erase(evb_);
170 DCHECK(fm.get() != nullptr);
171 ThreadLocalCache::erase(evb_);
173 while (fm->hasTasks()) {
174 fm->loopUntilNoReady();
183 FiberManager& getFiberManager(
185 const FiberManager::Options& opts) {
186 return ThreadLocalCache::get(evb, opts);