/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include namespace folly { namespace fibers { namespace detail { // ssize_t is a hash of FiberManager::Options template using Key = std::tuple; template Function makeOnEventBaseDestructionCallback(const Key& key); template class GlobalCache { public: template static FiberManager& get( const Key& key, EventBaseT& evb, const FiberManager::Options& opts) { return instance().template getImpl(key, evb, opts); } static std::unique_ptr erase(const Key& key) { return instance().eraseImpl(key); } private: GlobalCache() = default; // Leak this intentionally. During shutdown, we may call getFiberManager, // and want access to the fiber managers during that time. static GlobalCache& instance() { static auto ret = new GlobalCache(); return *ret; } template FiberManager& getImpl( const Key& key, EventBaseT& evb, const FiberManager::Options& opts) { bool constructed = false; SCOPE_EXIT { if (constructed) { evb.runOnDestruction(makeOnEventBaseDestructionCallback(key)); } }; std::lock_guard lg(mutex_); auto& fmPtrRef = map_[key]; if (!fmPtrRef) { constructed = true; auto loopController = std::make_unique(); loopController->attachEventBase(evb); fmPtrRef = std::make_unique( LocalType(), std::move(loopController), opts); } return *fmPtrRef; } std::unique_ptr eraseImpl(const Key& key) { std::lock_guard lg(mutex_); DCHECK_EQ(map_.count(key), 1u); auto ret = std::move(map_[key]); map_.erase(key); return ret; } std::mutex mutex_; std::unordered_map, std::unique_ptr> map_; }; constexpr size_t kEraseListMaxSize = 64; template class ThreadLocalCache { public: template static FiberManager& get( uint64_t token, EventBaseT& evb, const FiberManager::Options& opts) { return instance()->template getImpl(token, evb, opts); } static void erase(const Key& key) { for (auto& localInstance : instance().accessAllThreads()) { localInstance.eraseInfo_.withWLock([&](auto& info) { if (info.eraseList.size() >= kEraseListMaxSize) { info.eraseAll = true; } else { info.eraseList.push_back(key); } localInstance.eraseRequested_ = true; }); } } private: ThreadLocalCache() = default; struct ThreadLocalCacheTag {}; using ThreadThreadLocalCache = ThreadLocal; // Leak this intentionally. During shutdown, we may call getFiberManager, // and want access to the fiber managers during that time. static ThreadThreadLocalCache& instance() { static auto ret = new ThreadThreadLocalCache([]() { return new ThreadLocalCache(); }); return *ret; } template FiberManager& getImpl( uint64_t token, EventBaseT& evb, const FiberManager::Options& opts) { eraseImpl(); auto key = make_tuple(&evb, token, std::type_index(typeid(LocalT))); auto& fmPtrRef = map_[key]; if (!fmPtrRef) { fmPtrRef = &GlobalCache::template get(key, evb, opts); } DCHECK(fmPtrRef != nullptr); return *fmPtrRef; } void eraseImpl() { if (!eraseRequested_.load()) { return; } eraseInfo_.withWLock([&](auto& info) { if (info.eraseAll) { map_.clear(); } else { for (auto& key : info.eraseList) { map_.erase(key); } } info.eraseList.clear(); info.eraseAll = false; eraseRequested_ = false; }); } std::unordered_map, FiberManager*> map_; std::atomic eraseRequested_{false}; struct EraseInfo { bool eraseAll{false}; std::vector> eraseList; }; folly::Synchronized eraseInfo_; }; template Function makeOnEventBaseDestructionCallback( const Key& key) { return [key] { auto fm = GlobalCache::erase(key); DCHECK(fm.get() != nullptr); ThreadLocalCache::erase(key); }; } } // namespace detail template FiberManager& getFiberManagerT( EventBase& evb, const FiberManager::Options& opts) { return detail::ThreadLocalCache::get(0, evb, opts); } FiberManager& getFiberManager( folly::EventBase& evb, const FiberManager::Options& opts) { return detail::ThreadLocalCache::get(0, evb, opts); } FiberManager& getFiberManager( VirtualEventBase& evb, const FiberManager::Options& opts) { return detail::ThreadLocalCache::get(0, evb, opts); } FiberManager& getFiberManager( folly::EventBase& evb, const FiberManager::FrozenOptions& opts) { return detail::ThreadLocalCache::get( opts.token, evb, opts.options); } FiberManager& getFiberManager( VirtualEventBase& evb, const FiberManager::FrozenOptions& opts) { return detail::ThreadLocalCache::get( opts.token, evb, opts.options); } } // namespace fibers } // namespace folly