32#include <flow/util/action_registry.hpp>
33#include <flow/util/thread_lcl.hpp>
34#include <flow/log/log.hpp>
35#include <boost/unordered_map.hpp>
36#include <boost/unordered_set.hpp>
37#include <boost/unordered/unordered_flat_map.hpp>
252 private boost::noncopyable
504 template<
typename On_done_func>
506 On_done_func&& on_done_func);
564 flow::util::Thread_local_state_registry<Thread_cache>
m_caches;
630template<
typename On_done_func>
632 On_done_func&& on_done_func_arg)
634 using flow::util::this_thread_unique_token;
636 FLOW_LOG_SET_LOCKED_CONTEXT(log_ctx);
640 = [on_done_func_arg = std::move(on_done_func_arg), arena_id]
641 (
auto log_ctx)
mutable
647 on_done_func_arg(log_ctx, [&]()
652 assert(!on_done_func.empty());
658 if (state_per_thread.empty())
660 assert(!on_done_func.empty());
667 Arena_death_progress& arena_death_progress = (*arenas_death_row_map)[arena_id];
669 arena_death_progress.m_on_done_func = std::move(on_done_func);
670 on_done_func.clear();
672 for (const auto& [cache, nil] : state_per_thread)
674 arena_death_progress.m_caches_left_to_destroy.insert(cache);
682 for (
const auto& cache_and_nil : state_per_thread)
687 auto*
const cache = cache_and_nil.first;
689 if (this_thread_unique_token() == cache->thread_token())
691 FLOW_LOG_INFO_LOCKED(
"Jem_tcache[" << *cache <<
"]: "
692 "There was a typically-cross-thread tcache-destruction request "
693 "(arena [" << arena_id <<
"] being destroyed); "
694 "but we are in the relevant thread *now*, so we shall simply tcache-destroy the "
695 "tcache for that arena (if it exists) and skip the "
696 "flag mechanism; possibly even finish destroying arena.");
697 s_state.m_arenas_death_row_map.while_locked([&](auto* arenas_death_row_map_ptr)
699 auto& arenas_death_row_map = *arenas_death_row_map_ptr;
701 const auto it_arena_and_progress = arenas_death_row_map.find(arena_id);
702 assert((it_arena_and_progress != arenas_death_row_map.end())
703 &&
"What could have possibly already handled this thread's entry that we just inserted? Bug?");
709 auto& arena_death_progress = it_arena_and_progress->second;
710 auto& caches = arena_death_progress.m_caches_left_to_destroy;
715 assert(ok &&
"What could have possibly already handled this thread's entry that we just inserted? Bug?");
717 assert(on_done_func.empty() &&
"Sanity check of nearby algorithm.");
720 on_done_func = std::move(arena_death_progress.m_on_done_func);
721 arenas_death_row_map.erase(it_arena_and_progress);
725 const auto it_arena_and_tcache = cache->m_tcaches_by_arena.find(arena_id);
726 if (it_arena_and_tcache != cache->m_tcaches_by_arena.end())
728 const auto id = it_arena_and_tcache->second;
730 (
"Jem_tcache[" << *cache <<
"]: Indeed we do hold tcache [" << id <<
"] for that arena. "
731 "Proceeding with destruction to cross ourselves off the list. If that makes "
732 "the list empty (does it? = [" << (!on_done_func.empty()) <<
"]), "
733 "we shall imminently destroy arena.");
734 cache->destroy_tcache(id);
735 cache->m_tcaches_by_arena.erase(it_arena_and_tcache);
741 FLOW_LOG_INFO_LOCKED(
"Jem_tcache[" << *cache <<
"]: In outside thread (unique-token "
742 "[" << this_thread_unique_token() <<
"]) setting tcache-destruction-requested "
743 "flag on (arena [" << arena_id <<
"] being destroyed) for per-thread for thread with "
744 "unique-token [" << cache->thread_token() <<
"], "
745 "nickname [" << cache->thread_nickname() <<
"]. "
746 "Next time that thread polls for that flag being true, it will tcache-destroy the "
747 "relevant tcache if any and possibly finish destroying arena for real.");
748 cache->m_destroy_tcaches_requested_poll_flag.arm_next_poll();
753 if (!on_done_func.empty())
761 on_done_func(log_ctx);
jemalloc central thread cache manager, maintaining an explicitly identified tcache per allocating thr...
flow::util::Thread_token thread_token() const
Unique-token of thread in which *this was originally created via this_thread_cache() call.
void if_requested_destroy_tcaches_and_possibly_finish_arena_kills(bool assume_requested=false)
Perform deferred cross-thread tcache and arena-destruction as triggered by destroy_arena_safely() cal...
static constexpr arena_id_t S_NO_ARENA_ID
Value to always pass to id() or id_or_none(), if you do not wish for Thread_cache to worry about 1-to...
void flush_tcache(tcache_id_t id)
Flushes the given tcache as specified by its ID.
friend std::ostream & operator<<(std::ostream &, const Thread_cache &)
Prints string representation of the given Thread_cache to the given ostream.
static Static_state s_state
See doc header and especially member doc headers for Static_state.
flow::util::Thread_token m_thread_token
See thread_token().
std::string m_thread_nickname
See thread_nickname().
boost::unordered_flat_map< arena_id_t, tcache_id_t > m_tcaches_by_arena
See id(): for this thread, maps arena ID (that has been passed to id() at least once) to the jemalloc...
static constexpr tcache_id_t S_NO_TCACHE_ID
Special value returned by id_or_none() indicating no tcache yet exists for the given arena_id_t.
static void destroy_arena_safely(arena_id_t arena_id, const flow::log::Log_context_mt *log_ctx, On_done_func &&on_done_func)
Destroys (as by mallctl("arena.X.destroy")) the given jemalloc arena, as soon as it is safe without c...
~Thread_cache()
Destructor that runs in each thread (potentially even after main() – the thread had not been joined b...
Thread_cache(flow::log::Logger *logger_ptr)
Constructs cache for this thread as invoked internally by this_thread_cache().
static Thread_cache * this_thread_cache_or_null()
Returns pointer to the one cache-object in the calling thread, or null if none exists (this_thread_ca...
static Thread_cache * this_thread_cache()
Returns pointer to the one cache-object in the calling thread, first creating it if it is the first s...
flow::util::Poll_flag m_destroy_tcaches_requested_poll_flag
Poll-flag corresponding to Static_state::m_arenas_death_row_map Polled_shared_state pattern.
tcache_id_t id(arena_id_t arena_id)
Returns tcache ID of the tcache in this thread with a 1-1 relationship to the given arena,...
const std::string & thread_nickname() const
Thread nickname, as from flow::log::Logger::set_thread_info(), corresponding to thread in which *this...
void destroy_tcache(tcache_id_t id)
Flushes/destroys the given tcache.
tcache_id_t id_or_none(arena_id_t arena_id) const
Returns tcache ID of the tcache in this thread with a 1-1 relationship to the given arena,...
static void destroy_arena(arena_id_t arena_id, const flow::log::Log_context_mt *log_ctx)
Destroys the given arena now.
static void caches_set_logger(flow::log::Logger *logger_ptr)
Sets the Logger to use (or null to skip logging) subsequently for logging in static calls; non-static...
ipc::shm sub-module with the SHM-jemalloc SHM-provider. See ipc::shm doc header for introduction.
unsigned int arena_id_t
Arena ID as dictated by jemalloc.
unsigned int tcache_id_t
Thread cache ID type as dictated by jemalloc.
flow::Function< Signature > Function
Short-hand for polymorphic functor holder which is very common. This is essentially std::function.
For each arena currently being destroyed via destroy_arena_safely(), the state tracking the progress ...
boost::unordered_set< Thread_cache * > m_caches_left_to_destroy
Which Thread_cache extant objects have yet to invoke if_requested_destroy_tcaches_and_possibly_finish...
Function< void(const flow::log::Log_context_mt *)> m_on_done_func
Functor to invoke, and then immediately destroy, once m_caches_left_to_destroy is ....
Bundles static state used among the instances of this class; this allows us to ensure order of init/d...
flow::util::Polled_shared_state< boost::unordered_map< arena_id_t, Arena_death_progress > > m_arenas_death_row_map
State shared – and acted upon using the Polled_shared_state pattern – between extant Thread_cache per...
Static_state()
Constructor.
flow::util::Thread_local_state_registry< Thread_cache > m_caches
The work-horse: registry of per-thread Thread_cache objects, each holding 0+ jemalloc tcaches.
flow::util::Mutex_non_recursive m_arenas_death_row_map_on_done_func_mutex
Protects against concurrent execution of Arena_death_progress::m_on_done_func.