2 * Copyright 2017 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 = std::make_unique<EventBaseLoopController>();
67 loopController->attachEventBase(evb);
68 evb.runOnDestruction(new EventBaseOnDestructionCallback<EventBaseT>(evb));
71 std::make_unique<FiberManager>(std::move(loopController), opts);
77 std::unique_ptr<FiberManager> eraseImpl(EventBaseT& evb) {
78 std::lock_guard<std::mutex> lg(mutex_);
80 DCHECK_EQ(map_.count(&evb), 1u);
82 auto ret = std::move(map_[&evb]);
88 std::unordered_map<EventBaseT*, std::unique_ptr<FiberManager>> map_;
91 constexpr size_t kEraseListMaxSize = 64;
93 template <typename EventBaseT>
94 class ThreadLocalCache {
96 static FiberManager& get(EventBaseT& evb, const FiberManager::Options& opts) {
97 return instance()->getImpl(evb, opts);
100 static void erase(EventBaseT& evb) {
101 for (auto& localInstance : instance().accessAllThreads()) {
102 SYNCHRONIZED(info, localInstance.eraseInfo_) {
103 if (info.eraseList.size() >= kEraseListMaxSize) {
104 info.eraseAll = true;
106 info.eraseList.push_back(&evb);
108 localInstance.eraseRequested_ = true;
114 ThreadLocalCache() {}
116 struct ThreadLocalCacheTag {};
117 using ThreadThreadLocalCache =
118 ThreadLocal<ThreadLocalCache, ThreadLocalCacheTag>;
120 // Leak this intentionally. During shutdown, we may call getFiberManager,
121 // and want access to the fiber managers during that time.
122 static ThreadThreadLocalCache& instance() {
124 new ThreadThreadLocalCache([]() { return new ThreadLocalCache(); });
128 FiberManager& getImpl(EventBaseT& evb, const FiberManager::Options& opts) {
131 auto& fmPtrRef = map_[&evb];
133 fmPtrRef = &GlobalCache<EventBaseT>::get(evb, opts);
136 DCHECK(fmPtrRef != nullptr);
142 if (!eraseRequested_.load()) {
146 SYNCHRONIZED(info, eraseInfo_) {
150 for (auto evbPtr : info.eraseList) {
155 info.eraseList.clear();
156 info.eraseAll = false;
157 eraseRequested_ = false;
161 std::unordered_map<EventBaseT*, FiberManager*> map_;
162 std::atomic<bool> eraseRequested_{false};
165 bool eraseAll{false};
166 std::vector<EventBaseT*> eraseList;
169 folly::Synchronized<EraseInfo> eraseInfo_;
172 template <typename EventBaseT>
173 void EventBaseOnDestructionCallback<EventBaseT>::runLoopCallback() noexcept {
174 auto fm = GlobalCache<EventBaseT>::erase(evb_);
175 DCHECK(fm.get() != nullptr);
176 ThreadLocalCache<EventBaseT>::erase(evb_);
183 FiberManager& getFiberManager(
185 const FiberManager::Options& opts) {
186 return ThreadLocalCache<EventBase>::get(evb, opts);
189 FiberManager& getFiberManager(
190 VirtualEventBase& evb,
191 const FiberManager::Options& opts) {
192 return ThreadLocalCache<VirtualEventBase>::get(evb, opts);