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 template <typename EventBaseT>
30 class EventBaseOnDestructionCallback : public EventBase::LoopCallback {
32 explicit EventBaseOnDestructionCallback(EventBaseT& evb) : evb_(evb) {}
33 void runLoopCallback() noexcept override;
39 template <typename EventBaseT>
42 static FiberManager& get(EventBaseT& evb, const FiberManager::Options& opts) {
43 return instance().getImpl(evb, opts);
46 static std::unique_ptr<FiberManager> erase(EventBaseT& evb) {
47 return instance().eraseImpl(evb);
53 // Leak this intentionally. During shutdown, we may call getFiberManager,
54 // and want access to the fiber managers during that time.
55 static GlobalCache& instance() {
56 static auto ret = new GlobalCache();
60 FiberManager& getImpl(EventBaseT& evb, const FiberManager::Options& opts) {
61 std::lock_guard<std::mutex> lg(mutex_);
63 auto& fmPtrRef = map_[&evb];
66 auto loopController = make_unique<EventBaseLoopControllerT<EventBaseT>>();
67 loopController->attachEventBase(evb);
68 evb.runOnDestruction(new EventBaseOnDestructionCallback<EventBaseT>(evb));
70 fmPtrRef = make_unique<FiberManager>(std::move(loopController), opts);
76 std::unique_ptr<FiberManager> eraseImpl(EventBaseT& evb) {
77 std::lock_guard<std::mutex> lg(mutex_);
79 DCHECK_EQ(map_.count(&evb), 1u);
81 auto ret = std::move(map_[&evb]);
87 std::unordered_map<EventBaseT*, std::unique_ptr<FiberManager>> map_;
90 constexpr size_t kEraseListMaxSize = 64;
92 template <typename EventBaseT>
93 class ThreadLocalCache {
95 static FiberManager& get(EventBaseT& evb, const FiberManager::Options& opts) {
96 return instance()->getImpl(evb, opts);
99 static void erase(EventBaseT& evb) {
100 for (auto& localInstance : instance().accessAllThreads()) {
101 SYNCHRONIZED(info, localInstance.eraseInfo_) {
102 if (info.eraseList.size() >= kEraseListMaxSize) {
103 info.eraseAll = true;
105 info.eraseList.push_back(&evb);
107 localInstance.eraseRequested_ = true;
113 ThreadLocalCache() {}
115 struct ThreadLocalCacheTag {};
116 using ThreadThreadLocalCache =
117 ThreadLocal<ThreadLocalCache, ThreadLocalCacheTag>;
119 // Leak this intentionally. During shutdown, we may call getFiberManager,
120 // and want access to the fiber managers during that time.
121 static ThreadThreadLocalCache& instance() {
123 new ThreadThreadLocalCache([]() { return new ThreadLocalCache(); });
127 FiberManager& getImpl(EventBaseT& evb, const FiberManager::Options& opts) {
130 auto& fmPtrRef = map_[&evb];
132 fmPtrRef = &GlobalCache<EventBaseT>::get(evb, opts);
135 DCHECK(fmPtrRef != nullptr);
141 if (!eraseRequested_.load()) {
145 SYNCHRONIZED(info, eraseInfo_) {
149 for (auto evbPtr : info.eraseList) {
154 info.eraseList.clear();
155 info.eraseAll = false;
156 eraseRequested_ = false;
160 std::unordered_map<EventBaseT*, FiberManager*> map_;
161 std::atomic<bool> eraseRequested_{false};
164 bool eraseAll{false};
165 std::vector<EventBaseT*> eraseList;
168 folly::Synchronized<EraseInfo> eraseInfo_;
171 template <typename EventBaseT>
172 void EventBaseOnDestructionCallback<EventBaseT>::runLoopCallback() noexcept {
173 auto fm = GlobalCache<EventBaseT>::erase(evb_);
174 DCHECK(fm.get() != nullptr);
175 ThreadLocalCache<EventBaseT>::erase(evb_);
182 FiberManager& getFiberManager(
184 const FiberManager::Options& opts) {
185 return ThreadLocalCache<EventBase>::get(evb, opts);
188 FiberManager& getFiberManager(
189 VirtualEventBase& evb,
190 const FiberManager::Options& opts) {
191 return ThreadLocalCache<VirtualEventBase>::get(evb, opts);