unioil-loyalty-rn-app/ios/Pods/Flipper-Folly/folly/fibers/FiberManagerMap-inl.h

233 lines
6.2 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its 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 <memory>
#include <unordered_map>
#include <folly/Function.h>
#include <folly/ScopeGuard.h>
#include <folly/Synchronized.h>
#include <folly/ThreadLocal.h>
namespace folly {
namespace fibers {
namespace detail {
// ssize_t is a hash of FiberManager::Options
template <typename EventBaseT>
using Key = std::tuple<EventBaseT*, ssize_t, std::type_index>;
template <typename EventBaseT>
Function<void()> makeOnEventBaseDestructionCallback(const Key<EventBaseT>& key);
template <typename EventBaseT>
class GlobalCache {
public:
template <typename LocalT>
static FiberManager& get(
const Key<EventBaseT>& key,
EventBaseT& evb,
const FiberManager::Options& opts) {
return instance().template getImpl<LocalT>(key, evb, opts);
}
static std::unique_ptr<FiberManager> erase(const Key<EventBaseT>& 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 <typename LocalT>
FiberManager& getImpl(
const Key<EventBaseT>& key,
EventBaseT& evb,
const FiberManager::Options& opts) {
bool constructed = false;
SCOPE_EXIT {
if (constructed) {
evb.runOnDestruction(makeOnEventBaseDestructionCallback(key));
}
};
std::lock_guard<std::mutex> lg(mutex_);
auto& fmPtrRef = map_[key];
if (!fmPtrRef) {
constructed = true;
auto loopController = std::make_unique<EventBaseLoopController>();
loopController->attachEventBase(evb);
fmPtrRef = std::make_unique<FiberManager>(
LocalType<LocalT>(), std::move(loopController), opts);
}
return *fmPtrRef;
}
std::unique_ptr<FiberManager> eraseImpl(const Key<EventBaseT>& key) {
std::lock_guard<std::mutex> 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<Key<EventBaseT>, std::unique_ptr<FiberManager>> map_;
};
constexpr size_t kEraseListMaxSize = 64;
template <typename EventBaseT>
class ThreadLocalCache {
public:
template <typename LocalT>
static FiberManager& get(
uint64_t token, EventBaseT& evb, const FiberManager::Options& opts) {
return instance()->template getImpl<LocalT>(token, evb, opts);
}
static void erase(const Key<EventBaseT>& 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<ThreadLocalCache, ThreadLocalCacheTag>;
// 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 <typename LocalT>
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<EventBaseT>::template get<LocalT>(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<Key<EventBaseT>, FiberManager*> map_;
std::atomic<bool> eraseRequested_{false};
struct EraseInfo {
bool eraseAll{false};
std::vector<Key<EventBaseT>> eraseList;
};
folly::Synchronized<EraseInfo> eraseInfo_;
};
template <typename EventBaseT>
Function<void()> makeOnEventBaseDestructionCallback(
const Key<EventBaseT>& key) {
return [key] {
auto fm = GlobalCache<EventBaseT>::erase(key);
DCHECK(fm.get() != nullptr);
ThreadLocalCache<EventBaseT>::erase(key);
};
}
} // namespace detail
template <typename LocalT>
FiberManager& getFiberManagerT(
EventBase& evb, const FiberManager::Options& opts) {
return detail::ThreadLocalCache<EventBase>::get<LocalT>(0, evb, opts);
}
FiberManager& getFiberManager(
folly::EventBase& evb, const FiberManager::Options& opts) {
return detail::ThreadLocalCache<EventBase>::get<void>(0, evb, opts);
}
FiberManager& getFiberManager(
VirtualEventBase& evb, const FiberManager::Options& opts) {
return detail::ThreadLocalCache<VirtualEventBase>::get<void>(0, evb, opts);
}
FiberManager& getFiberManager(
folly::EventBase& evb, const FiberManager::FrozenOptions& opts) {
return detail::ThreadLocalCache<EventBase>::get<void>(
opts.token, evb, opts.options);
}
FiberManager& getFiberManager(
VirtualEventBase& evb, const FiberManager::FrozenOptions& opts) {
return detail::ThreadLocalCache<VirtualEventBase>::get<void>(
opts.token, evb, opts.options);
}
} // namespace fibers
} // namespace folly