39#include <flow/util/linked_hash_map.hpp>
40#include <flow/util/thread_lcl.hpp>
41#include <flow/util/util.hpp>
42#include <flow/util/stat/stat_set.hpp>
43#include <flow/util/stat/stat_set_list.hpp>
44#include <flow/log/log.hpp>
45#include <flow/log/config.hpp>
46#include <flow/async/async_fwd.hpp>
47#include <boost/thread/future.hpp>
48#include <boost/thread/thread_only.hpp>
49#include <boost/unordered/unordered_flat_set.hpp>
50#include <boost/unordered/unordered_flat_map.hpp>
51#include <boost/range/join.hpp>
52#include <boost/range/adaptor/filtered.hpp>
53#include <boost/range/adaptor/transformed.hpp>
54#include <boost/range/adaptor/map.hpp>
55#include <boost/range/algorithm/for_each.hpp>
208template<
typename Shm_arena_t>
211 private boost::noncopyable
287 flow::log::Logger*
set_logger(flow::log::Logger* logger_ptr);
322 template<
typename Deleter_func_obj>
325 void* addr, Deleter_func_obj&& del_func);
420 template<
typename On_done_func>
422 On_done_func&& on_done_func);
450 using Own = boost::movelib::unique_ptr<T>;
515 flow::util::Linked_hash_map<use_ct_idx_t, Object>
m_objs;
581 flow::util::Polled_shared_state<boost::unordered_flat_map<collection_id_t, Arena_forget_progress>>
605 template<
bool,
typename Shm_arena2>
688 boost::promise<void>* done_with_exiting_thread_obj_db);
953template<
typename Shm_arena_t>
956 private boost::noncopyable
999 flow::log::Logger*
set_logger(flow::log::Logger* logger_ptr);
1097 template<
typename T>
1098 using Own = boost::movelib::unique_ptr<T>;
1137 flow::util::Polled_shared_state<boost::unordered_flat_map<
collection_id_t,
1138 boost::unordered_flat_set<Thread_lcl_obj_db_client*>>>
1152 template<
bool,
typename Shm_arena2>
1279template<
typename Shm_arena_t>
1282template<
typename Shm_arena_t>
1287template<
typename Shm_arena_t>
1292 m_skip_fast_path_verbose_logging(false),
1294 m_thread_token(flow::util::this_thread_unique_token()),
1295 m_locked_gap_mutex(false)
1297 using boost::this_thread::at_thread_exit;
1305 at_thread_exit([
this]()
1330 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: The Gap mutex: locking for this TL-object.");
1336template<
typename Shm_arena_t>
1339 using flow::util::Thread;
1340 using flow::log::Logger;
1341 using flow::log::Sev;
1342 using flow::util::ostream_op_string;
1343 using flow::util::Lock_guard;
1344 using flow::util::Mutex_recursive;
1345 using flow::util::stat::store;
1346 using flow::async::reset_this_thread_pinning;
1347 using Log_config = flow::log::Config;
1348 using boost::promise;
1349 using std::optional;
1351 const auto this_thread_unique_token = flow::util::this_thread_unique_token();
1352 if (this_thread_unique_token != m_thread_token)
1354 FLOW_LOG_WARNING_LOCKED
1355 (
"Tl_obj_db_admin: "
1356 "Shutting down from different thread (unique-token [" << this_thread_unique_token <<
"]) "
1357 "than the one (unique-token [" << m_thread_token <<
"]) that created us; "
1358 "bailing out. Honestly this is strange; we are a `static` thread-local registry, and per-thread "
1359 "dtors should run before the central static object's. In any case, this can conceivably -- "
1360 "in the absence of bugs -- only occur when program is exiting, at which "
1361 "point graceful shutdown of resources is arguably less essential.");
1362 assert((!m_locked_gap_mutex) &&
"Tl_obj_db_admin: We have a misdesign/bug; something is off thread-wise.");
1390 optional<Lock_guard<Mutex_recursive>> gap_lock;
1391 if (m_locked_gap_mutex)
1393 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: The Gap mutex is locked: adopting until dtor returns.");
1398 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: The Gap mutex is *not* locked; presumably this is the OS/main thread; "
1399 "probably application exit()ing/typically post-main(); no real danger of concurrent "
1456 if_requested_forget_arena_related_resources(
true);
1459 unused_obj_scan(
true,
false);
1460 if (inactive_arenas_scan())
1464 if (!m_collection_dbs.empty())
1477 for (
const auto& [collection_id, collection_db] : m_collection_dbs)
1493 store(&collection_db->m_stats_shard.m_live_obj.m_live_object_zombies, 0);
1496 collection_db->m_stats_shard);
1506 if (!m_locked_gap_mutex)
1518 FLOW_LOG_WARNING_LOCKED(
"Tl_obj_db_admin: OS/main thread's obj-DB is being destroyed at program exit (static "
1519 "destruction); yet live objects remain in at least one arena. Too late to start a "
1520 "degraded-admin thread to reap them later; so leaking them. Were all arenas destroyed "
1521 "before main() returned? This is interesting; recommend looking into what exactly "
1522 "happened to lead to this occurrence.");
1612 promise<void> init_done;
1614 auto& deg_mutex = s_state.m_degraded_admin_threads_mutex;
1615 auto& deg_threads = s_state.m_degraded_admin_threads;
1619 Lock_guard<std::remove_reference_t<
decltype(deg_mutex)>> deg_lock{deg_mutex};
1621 if (deg_threads.empty())
1623 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: Degraded-admin thread starting to take over from current "
1624 "exiting thread; and it is the first such thread; so registering std::atexit() handler "
1625 "that will, as needed, at program std::exit() (main() return) join any such thread(s) "
1626 "remaining at that time.");
1627 std::atexit(atexit_degraded_admin_threads_join);
1631 sev_override = *(Log_config::this_thread_verbosity_override());
1633 deg_threads.emplace_back(
new Thread{[&]()
1636 const auto sev_override_auto = Log_config::this_thread_verbosity_override_auto(sev_override);
1641 log_while_locked([&](
auto&& get_logger,
auto&&)
1643 Logger::this_thread_set_logged_nickname(ostream_op_string(
"JAd-",
1644 m_thread_token), get_logger());
1647 reset_this_thread_pinning();
1649 this_thread_obj_db()
1650 ->degraded_admin_thread_body(
this,
1657 init_done.get_future().wait();
1662template<
typename Shm_arena_t>
1665 return s_state.m_obj_db_registry.this_thread_state();
1668template<
typename Shm_arena_t>
1673 const auto obj_db = s_state.m_obj_db_registry.this_thread_state_or_null();
1676 obj_db->if_requested_forget_arena_related_resources();
1677 obj_db->unused_obj_scan(
false,
false);
1681template<
typename Shm_arena_t>
1684 s_state.m_obj_db_registry.set_logger(logger_ptr);
1687template<
typename Shm_arena_t>
1695 m_skip_fast_path_verbose_logging.store(!(logger_ptr
1696 && logger_ptr->should_log(flow::log::Sev::S_TRACE, Log_component::S_SHM)),
1697 std::memory_order_relaxed);
1699 return prev_logger_ptr;
1702template<
typename Shm_arena_t>
1705 return m_skip_fast_path_verbose_logging.load(std::memory_order_relaxed);
1708template<
typename Shm_arena_t>
1709template<
typename Deleter_func_obj>
1714 obj_db_aux_pool_stats,
1715 void* addr, Deleter_func_obj&& del_func)
1717 using flow::util::Lock_guard;
1718 using flow::util::stat::fetch_add;
1719 using std::optional;
1720 using std::in_place;
1722 auto& lend_tracker_pool_id = *lend_tracker_pool_id_ptr;
1723 const auto collection_id = shm_arena->get_id();
1742 const auto it_collection_id_and_db = m_collection_dbs.find(collection_id);
1743 if (it_collection_id_and_db == m_collection_dbs.end())
1747 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: For arena/collection (collection-ID [" << collection_id <<
"]), "
1748 "first constructed-obj in this thread; will create this thread+arena's lend-tracker SHM-pool "
1749 "(pool-ID [" << lend_tracker_pool_id <<
"]) and related tracking structures in heap.");
1752 lend_tracker_pool_id,
1753 optional<Lend_tracker_pool>
1755 this, &m_skip_fast_path_verbose_logging,
1759 obj_db_aux_pool_stats,
1760 shm_arena->get_permissions()},
1766 Lock_guard<
decltype(m_collection_dbs_mutex)> lock{m_collection_dbs_mutex};
1767 m_collection_dbs[collection_id].reset(collection_db);
1773 collection_db = it_collection_id_and_db->second.get();
1784 assert((!obj.
m_del_func) &&
"Double-used use_ct_idx somehow? Tracking bug? Investigate!");
1789 if (!skip_fast_path_verbose_logging())
1791 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), constructed-obj "
1792 "registered in lend-tracker SHM-pool and related tracking structures in heap; use count = 1; "
1793 "addr = [" << addr <<
"]; use-count index in lend-tracker-pool = [" << *use_ct_idx <<
"]; "
1794 "lend-tracker-pool ID = [" << lend_tracker_pool_id <<
"].");
1801 fetch_add(&owner_obj_stats.m_construct_count, 1);
1802 fetch_add(&owner_obj_stats.m_live_handle_groups, 1);
1803 fetch_add(&stats_shard.m_live_obj.m_live_objects, 1);
1808template<
typename Shm_arena_t>
1811 using flow::util::stat::fetch_add;
1812 using flow::util::stat::fetch_sub;
1815 assert(collection_db
1816 &&
"This thread did constructing_obj(), and we should only be called from same thread, "
1817 "so how could the arena not have an entry yet?");
1819 auto use_ct_idx_copy = use_ct_idx;
1825 if (collection_db->m_lend_tracker_pool->use_count_dec_admin(&use_ct_idx_copy) == 0)
1827 auto& objs = collection_db->m_objs;
1828 const auto obj_it = objs.find(use_ct_idx);
1829 assert(obj_it != objs.end());
1830 const auto& obj = obj_it->second;
1832 if (!skip_fast_path_verbose_logging())
1834 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), constructed-obj "
1835 "disposer executed in same thread as we cted it; upon decrementing use count it reached 0; "
1836 "we shall immediately delete it and remove it from in-heap tracking and removed it from "
1837 "lend-tracker SHM-pool; addr = [" << obj.m_addr <<
"]; use-count index in "
1838 "lend-tracker-pool = [" << use_ct_idx <<
"]; "
1839 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
1843 obj.m_del_func(obj.m_addr, collection_db->m_shm_arena);
1849 auto& stats_shard = collection_db->m_stats_shard;
1850 auto& owner_obj_stats = stats_shard.m_owner_obj;
1852 fetch_add(&owner_obj_stats.m_disposer_count, 1);
1853 fetch_sub(&owner_obj_stats.m_live_handle_groups, 1);
1855 if (use_ct_idx_copy == 0)
1857 fetch_add(&owner_obj_stats.m_destroy_count, 1);
1858 fetch_add(&stats_shard.m_owner_obj_arena_lend.m_sync_destroy_count, 1);
1859 fetch_sub(&stats_shard.m_live_obj.m_live_objects, 1);
1864template<
typename Shm_arena_t>
1867 using flow::util::stat::fetch_add;
1870 assert(collection_db
1871 &&
"This thread did constructing_obj(), and we should only be called from same thread, "
1872 "so how could the arena not have an entry yet?");
1878 const auto use_ct = collection_db->m_lend_tracker_pool->use_count_inc(use_ct_idx);
1880 if (!skip_fast_path_verbose_logging())
1882 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), constructed-obj "
1883 "being lent in same thread as we cted it; upon incrementing use count it reached "
1884 "[" << use_ct <<
"]; addr = [" << collection_db->m_objs[use_ct_idx].m_addr <<
"]; use-count "
1885 "index in lend-tracker-pool = [" << use_ct_idx <<
"]; "
1886 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
1891 fetch_add(&collection_db->m_stats_shard.m_lend_obj.m_lend_count, 1);
1895template<
typename Shm_arena_t>
1899 using flow::util::stat::fetch_add;
1900 using flow::util::stat::fetch_sub;
1906 vector<use_ct_idx_t> unused_ct_idxs;
1907 typename decltype(Collection_db::m_objs)::Iterator obj_it;
1908 typename decltype(Collection_db::m_objs)::Reverse_iterator obj_r_it;
1909 for (
const auto& collection_id_and_db : m_collection_dbs)
1914 const auto collection_id = collection_id_and_db.first;
1915 const auto& collection_db = collection_id_and_db.second;
1917 auto& lend_tracker_pool_opt = collection_db->m_lend_tracker_pool;
1918 if (!lend_tracker_pool_opt)
1923 auto& lend_tracker_pool = *lend_tracker_pool_opt;
1925 auto& stats_shard = collection_db->m_stats_shard;
1926 auto& zombie_obj_reaper_stats = draining ? stats_shard.m_zombie_obj_reaper_drain
1927 : stats_shard.m_zombie_obj_reaper_main;
1929 fetch_add(&zombie_obj_reaper_stats.m_scans, 1);
1930 exhaustive && fetch_add(&zombie_obj_reaper_stats.m_exhaustive_scans, 1);
1933 auto& objs = collection_db->m_objs;
1938 signed int n_unused;
1941 n_unused = std::numeric_limits<
decltype(n_unused)>::max();
1945 n_unused = lend_tracker_pool.n_unused();
1959 auto n_unused_saved = n_unused;
1960 enum { HINTS_SUFFICIENT, FIFO_SUFFICIENT, EXHAUSTED } scan_outcome;
1965 Zombie_obj_reaper_stats::Events<false> batched_stats;
1966 unsigned int batched_destroy_count = 0;
1968 lend_tracker_pool.emit_unused_ct_idx_hints(&unused_ct_idxs);
1969 batched_stats.m_hints_checked += unused_ct_idxs.size();
1970 for (
auto unused_ct_idx_possibly : unused_ct_idxs)
1972 obj_it = objs.find(unused_ct_idx_possibly);
1973 if (obj_it == objs.end())
1976 if (!skip_fast_path_verbose_logging())
1978 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), piggy-scan "
1979 "got hint about live object with use-count=0; but we'd already deleted it; it happens; "
1980 "(former) use-count index in lend-tracker-pool = [" << unused_ct_idx_possibly <<
"]; "
1981 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
1985 ++batched_stats.m_hints_stale;
1990 auto use_ct_idx_copy = unused_ct_idx_possibly;
1991 if (!lend_tracker_pool.use_count_return_if_unused(&use_ct_idx_copy))
2009 if (!skip_fast_path_verbose_logging())
2011 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), piggy-scan "
2012 "got hint about live object with use-count=0; and one is live at that index; but it "
2013 "must be another object by now, as its use-count is >0; "
2014 "(former) use-count index in lend-tracker-pool = [" << unused_ct_idx_possibly <<
"]; "
2015 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
2019 ++batched_stats.m_hints_stale_and_slots_reused;
2025 auto& obj = obj_it->second;
2027 if (!skip_fast_path_verbose_logging())
2029 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), piggy-scan "
2030 "got hint about live object with use-count=0; and one is live at that index; and the "
2031 "use-count *is* zero; we shall immediately delete it from in-heap tracking/removed it "
2032 "from lend-tracker SHM-pool; addr = [" << obj.m_addr <<
"]; use-count index in "
2033 "lend-tracker-pool = [" << unused_ct_idx_possibly <<
"]; "
2034 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
2038 obj.m_del_func(obj.m_addr, collection_db->m_shm_arena);
2040 ++batched_destroy_count;
2048 scan_outcome = HINTS_SUFFICIENT;
2054 obj_r_it = objs.oldest();
2060 scan_outcome = EXHAUSTED;
2061 bool done = obj_r_it == objs.past_newest();
2064 ++batched_stats.m_fifo_objs_checked;
2066 auto unused_ct_idx_possibly = obj_r_it->first;
2067 auto use_ct_idx_copy = unused_ct_idx_possibly;
2068 if (!lend_tracker_pool.use_count_return_if_unused(&use_ct_idx_copy))
2070 done = ((++obj_r_it) == objs.past_newest());
2072 ++batched_stats.m_fifo_objs_live;
2080 auto obj_it_to_erase = obj_r_it.base();
2083 auto& obj = obj_it_to_erase->second;
2085 if (!skip_fast_path_verbose_logging())
2087 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), piggy-scan "
2088 "was performing oldest-to-newest scan phase through live objects; and one's "
2089 "use-count *is* zero; we shall immediately delete it from in-heap tracking/removed it "
2090 "from lend-tracker SHM-pool; addr = [" << obj.m_addr <<
"]; use-count index in "
2091 "lend-tracker-pool = [" << unused_ct_idx_possibly <<
"]; "
2092 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
2096 obj.m_del_func(obj.m_addr, collection_db->m_shm_arena);
2097 objs.erase(obj_it_to_erase);
2098 ++batched_destroy_count;
2101 if ((--n_unused) == 0)
2103 done =
true; scan_outcome = FIFO_SUFFICIENT;
2107 done = (obj_r_it == objs.past_newest());
2111 if (!skip_fast_path_verbose_logging())
2113 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), piggy-scan "
2114 "was performing oldest-to-newest scan phase through live objects; "
2115 "pre-scan unused-count was [" << n_unused_saved <<
"] (exhaustive scan? = "
2116 "[" << exhaustive <<
"]); we then found enough to decrement that temp-count-copy to "
2117 "[" << n_unused <<
"]; delta = [" << (n_unused_saved - n_unused) <<
"]; "
2118 "outcome{HINTS_SUFFICIENT|FIFO_SUFFICIENT|EXHAUSTED} = [" <<
int(scan_outcome) <<
"].");
2136 zombie_obj_reaper_stats.m_histo_zombie_ct_estimate.record_value(n_unused_saved);
2139 atomic<uint64_t>* tgt_ctr = {};
2140 switch (scan_outcome)
2142 case HINTS_SUFFICIENT: tgt_ctr = &zombie_obj_reaper_stats.m_hints_sufficient_scans;
break;
2143 case FIFO_SUFFICIENT: tgt_ctr = &zombie_obj_reaper_stats.m_fifo_scans;
break;
2144 case EXHAUSTED: tgt_ctr = &zombie_obj_reaper_stats.m_fifo_exhausted_scans;
2149 fetch_add(tgt_ctr, 1);
2153 (batched_stats.m_hints_checked == 0)
2154 || fetch_add(&zombie_obj_reaper_stats.m_events.m_hints_checked,
2155 batched_stats.m_hints_checked);
2156 (batched_stats.m_hints_stale == 0)
2157 || fetch_add(&zombie_obj_reaper_stats.m_events.m_hints_stale,
2158 batched_stats.m_hints_stale);
2159 (batched_stats.m_hints_stale_and_slots_reused == 0)
2160 || fetch_add(&zombie_obj_reaper_stats.m_events.m_hints_stale_and_slots_reused,
2161 batched_stats.m_hints_stale_and_slots_reused);
2162 (batched_stats.m_fifo_objs_checked == 0)
2163 || fetch_add(&zombie_obj_reaper_stats.m_events.m_fifo_objs_checked,
2164 batched_stats.m_fifo_objs_checked);
2165 (batched_stats.m_fifo_objs_live == 0)
2166 || fetch_add(&zombie_obj_reaper_stats.m_events.m_fifo_objs_live,
2167 batched_stats.m_fifo_objs_live);
2168 if (batched_destroy_count != 0)
2170 fetch_add(&stats_shard.m_owner_obj.m_destroy_count, batched_destroy_count);
2171 draining && fetch_add(&stats_shard.m_owner_obj_arena_lend.m_drain_destroy_count, batched_destroy_count);
2172 fetch_sub(&stats_shard.m_live_obj.m_live_objects, batched_destroy_count);
2178template<
typename Shm_arena_t>
2181 using flow::util::Lock_guard;
2187 bool no_live_objs_left =
true;
2188 for (
const auto& collection_id_and_db : m_collection_dbs)
2191 const auto collection_id = collection_id_and_db.first;
2192 const auto& collection_db = collection_id_and_db.second;
2194 if (!collection_db->m_objs.empty())
2197 no_live_objs_left =
false;
2212 Lock_guard<
decltype(m_collection_dbs_mutex)> lock{m_collection_dbs_mutex};
2214 auto& lend_tracker_pool_opt = collection_db->m_lend_tracker_pool;
2215 if (lend_tracker_pool_opt)
2217 FLOW_LOG_INFO_LOCKED
2218 (
"Tl_obj_db_admin: Thread exiting; after exhaustive scan of live objects constructed in this "
2219 "thread none remain for arena/collection (ID [" << collection_id <<
"]); hence "
2220 "deleting lend-tracker SHM-pool (leaving stats alone, as they keep counting). If we can do "
2221 "this for *all* relevant arenas/IDs, this thread can exit peacefully. Else will spawn, or "
2222 "keep running, degraded-admin thread.");
2223 lend_tracker_pool_opt.reset();
2243 return no_live_objs_left;
2246template<
typename Shm_arena_t>
2247template<
typename On_done_func>
2249 flow::log::Logger* logger_ptr,
2250 On_done_func&& on_done_func)
2254 using flow::util::this_thread_unique_token;
2256 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_SHM);
2258 bool done_synchronously =
true;
2262 s_state.m_obj_db_registry.while_locked([&](
const auto& obj_db_per_thread)
2264 if (obj_db_per_thread.empty())
2266 assert(done_synchronously);
2270 done_synchronously =
false;
2272 s_state.m_arenas_to_forget_map.while_locked([&](
auto* arenas_to_forget_map_ptr)
2274 auto& arenas_to_forget_map = *arenas_to_forget_map_ptr;
2276 Arena_forget_progress& arena_forget_progress = arenas_to_forget_map[collection_id];
2277 auto& db_set = arena_forget_progress.m_dbs_that_still_must_forget;
2278 auto& saved_on_done_func = arena_forget_progress.m_on_done_func;
2280 assert(db_set.empty() && saved_on_done_func.empty()
2281 &&
"forgetting_shm_arena() called twice on the same owner+collection ID? Bug?");
2283 saved_on_done_func = std::move(on_done_func);
2285 for (const auto& [obj_db, nil] : obj_db_per_thread)
2287 db_set.insert(obj_db);
2298 for (
const auto& obj_db_and_nil : obj_db_per_thread)
2301 const auto obj_db = obj_db_and_nil.first;
2303 if (this_thread_unique_token() == obj_db->m_thread_token)
2305 s_state.m_arenas_to_forget_map.while_locked([&](auto* arenas_to_forget_map_ptr)
2307 auto& arenas_to_forget_map = *arenas_to_forget_map_ptr;
2309 const auto it_arena_and_progress = arenas_to_forget_map.find(collection_id);
2310 assert((it_arena_and_progress != arenas_to_forget_map.end())
2311 &&
"What could have possibly already handled this thread's entry that we just inserted? Bug?");
2313 auto& arena_forget_progress = it_arena_and_progress->second;
2314 auto& db_set = arena_forget_progress.m_dbs_that_still_must_forget;
2316 const bool ok = 1 ==
2318 db_set.erase(obj_db);
2319 assert(ok &&
"What could have possibly already handled this thread's entry that we just inserted? Bug?");
2321 const bool last_one = db_set.empty();
2324 FLOW_LOG_INFO(
"Tl_obj_db_admin: Local arena/collection [" << collection_id <<
"] is being "
2325 "forgotten; during the request opportunistically scanned caller thread's per-thread "
2326 "in-heap structures, and this was the last (only) such per-thread structure that "
2327 "needed handling; so this immediately completes the "
2328 "forgetting of that arena/collection among the obj-DB per-thread admins; "
2329 "no async work is left.");
2331 arenas_to_forget_map.erase(it_arena_and_progress);
2332 done_synchronously = true;
2338 obj_db->forget_shm_arena(collection_id, last_one);
2343 FLOW_LOG_TRACE(
"Tl_obj_db_admin: In outside thread setting arena-forgetting-requested flag on "
2344 "(local arena/collection [" << collection_id <<
"] "
2345 "is being forgotten) for per-thread admin for thread with unique-token "
2346 "[" << obj_db->m_thread_token <<
"]. Next time that thread polls for that flag being "
2347 "true, it will forget/deinit the stuff relevant to that arena/collection.");
2348 obj_db->m_forget_resources_requested_poll_flag.arm_next_poll();
2357 Finalized_shards<Shm_arena>::get_instance().stats_erase(collection_id);
2360 return done_synchronously;
2371template<
typename Shm_arena_t>
2379 if ((!assume_requested) && (!m_forget_resources_requested_poll_flag.poll_armed()))
2388 vector<
decltype(Arena_forget_progress::m_on_done_func)> on_done_funcs_to_execute;
2389 s_state.m_arenas_to_forget_map.while_locked([&](
auto* arenas_to_forget_map_ptr)
2391 auto& arenas_to_forget_map = *arenas_to_forget_map_ptr;
2392 vector<collection_id_t> finished_collection_ids;
2394 for (
auto& collection_id_and_progress : arenas_to_forget_map)
2397 const auto collection_id = collection_id_and_progress.first;
2398 auto& arena_forget_progress = collection_id_and_progress.second;
2400 auto& db_set = arena_forget_progress.m_dbs_that_still_must_forget;
2401 if (db_set.erase(
this) == 0)
2407 const bool last_one = db_set.empty();
2409 forget_shm_arena(collection_id, last_one);
2413 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: Local arena/collection [" << collection_id <<
"] is being "
2414 "forgotten; opportunistically scanned our per-thread in-heap structures, and this was "
2415 "the last such per-thread structure that needed handling; so this completes the "
2416 "forgetting of that arena/collection among the obj-DB per-thread admins.");
2417 finished_collection_ids.push_back(collection_id);
2419 on_done_funcs_to_execute.emplace_back(std::move(arena_forget_progress.m_on_done_func));
2420 arena_forget_progress.m_on_done_func.clear();
2425 for (
const auto collection_id : finished_collection_ids)
2427 arenas_to_forget_map.erase(collection_id);
2431 for (
auto& on_done_func : on_done_funcs_to_execute)
2433 on_done_func(
static_cast<const Log_context_mt*
>(
this));
2437template<
typename Shm_arena_t>
2440 using flow::util::Lock_guard;
2444 const auto it_collection_id_and_db = m_collection_dbs.find(collection_id);
2445 if (it_collection_id_and_db == m_collection_dbs.end())
2447 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: Local arena/collection [" << collection_id <<
"] is being "
2448 "forgotten; so will delete all live objects (even if use-count>0); "
2449 "close any relevant lend-tracker SHM-pool admin handles (+unlink name) "
2450 "we've opened; and forget related tracking structures in heap... but we have none so no-op; "
2451 "are we the last per-thread obj-DB that was remaining to do this? = [" << last_one <<
"].");
2456 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: Local arena/collection [" << collection_id <<
"] is being "
2457 "forgotten; so will delete all live objects (even if use-count>0); "
2458 "close any relevant lend-tracker SHM-pool admin handles (+unlink name) "
2459 "we've opened; and forget related tracking structures in heap; are we the last "
2460 "per-thread obj-DB that was remaining to do this? = [" << last_one <<
"].");
2462 const auto collection_db = std::move(it_collection_id_and_db->second);
2463 for (
const auto& use_ct_idx_and_obj : collection_db->m_objs)
2466 const auto use_ct_idx = use_ct_idx_and_obj.first;
2467 const auto& obj = use_ct_idx_and_obj.second;
2472 const auto use_ct_informational = collection_db->m_lend_tracker_pool->use_count(use_ct_idx);
2473 if (use_ct_informational == 0)
2475 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), while forgetting "
2476 "it, deleting live object with use-count=0; addr = [" << obj.m_addr <<
"]; use-count "
2477 "index in lend-tracker-pool = [" << use_ct_idx <<
"]; "
2478 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
2482 FLOW_LOG_WARNING_LOCKED(
"Tl_obj_db_admin: For arena/collection (ID [" << collection_id <<
"]), while forgetting "
2483 "it, deleting live object with non-zero use-count [" << use_ct_informational <<
"]; "
2484 "perhaps opposing process crashed/session went down/etc. "
2485 "Addr = [" << obj.m_addr <<
"]; use-count index in "
2486 "lend-tracker-pool = [" << use_ct_idx <<
"]; "
2487 "lend-tracker-pool ID = [" << collection_db->m_lend_tracker_pool_id <<
"].");
2490 obj.m_del_func(obj.m_addr, collection_db->m_shm_arena);
2510 Lock_guard<
decltype(m_collection_dbs_mutex)> lock{m_collection_dbs_mutex};
2511 m_collection_dbs.erase(it_collection_id_and_db);
2523template<
typename Shm_arena_t>
2525 boost::promise<void>*
2526 done_with_exiting_thread_obj_db)
2528 using flow::Fine_clock;
2529 using boost::chrono::milliseconds;
2530 using boost::chrono::seconds;
2531 using boost::chrono::round;
2532 namespace this_thread = flow::util::this_thread;
2537 const auto exiting_thread_token = exiting_thread_obj_db->
m_thread_token;
2538 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: Degraded-admin thread started to take over from thread with unique-token "
2539 "[" << exiting_thread_token <<
"] (and/or see the present thread's nickname) "
2540 "which is exiting and will exit shortly, but "
2541 "not all live objects constructed by that thread have become unused, so we must keep "
2542 "deleting them when possible in this new thread; once all gone: exit this thread too. "
2543 "This new thread-local object has moved all live-object data from the exiting thread's.");
2550 flow::util::Lock_guard<
decltype(m_collection_dbs_mutex)> lock{m_collection_dbs_mutex};
2567 for (
const auto& [nil, collection_db] : m_collection_dbs)
2569 auto& lend_tracker_pool_opt = collection_db->m_lend_tracker_pool;
2570 if (lend_tracker_pool_opt)
2572 lend_tracker_pool_opt->update_log_context(
this, &m_skip_fast_path_verbose_logging);
2578 exiting_thread_obj_db =
nullptr;
2579 done_with_exiting_thread_obj_db->set_value();
2581 auto last_info_log_when = Fine_clock::now();
2584 this_thread::sleep_for(PERIOD_BTWN_CHECKS);
2586 const auto now = Fine_clock::now();
2587 if ((now - last_info_log_when) >= PERIOD_BTWN_INFO_LOGS)
2589 last_info_log_when = now;
2590 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: Informing you periodically: "
2591 "Degraded-admin thread still alive; # of arenas/collections with 1+ live objects: "
2592 "[" << m_collection_dbs.size() <<
"]. We scan this thread's obj-DB every "
2593 "[" << round<milliseconds>(PERIOD_BTWN_CHECKS) <<
"] which includes right now.");
2603 if_requested_forget_arena_related_resources(
true);
2605 unused_obj_scan(
false,
true);
2624 while (!inactive_arenas_scan());
2626 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_admin: Degraded-admin thread started to take over from thread with unique-token "
2627 "[" << exiting_thread_token <<
"]: Done! Exiting thread.");
2630template<
typename Shm_arena_t>
2635 for (
auto& thread : s_state.m_degraded_admin_threads)
2637 if (thread->joinable())
2646template<
typename Shm_arena_t>
2650 using flow::util::stat::store;
2651 using flow::util::Lock_guard;
2659 Lock_guard<
decltype(m_collection_dbs_mutex)> lock{m_collection_dbs_mutex};
2660 const auto it_collection_id_and_db = m_collection_dbs.find(collection_id);
2661 if (it_collection_id_and_db == m_collection_dbs.end())
2667 Collection_db& collection_db = *it_collection_id_and_db->second;
2671 if (lend_tracker_pool_opt)
2675 const signed int n_unused = lend_tracker_pool_opt->n_unused();
2676 store(&stats_shard.m_live_obj.m_live_object_zombies, (n_unused < 0) ? 0 : n_unused);
2681 store(&stats_shard.m_live_obj.m_live_object_zombies, 0);
2684 return &stats_shard;
2718template<
typename Shm_arena_t>
2720 m_obj_db_registry(nullptr,
"Thread_lcl_obj_db_admin")
2722 Set_logger_registry::register_action([](flow::log::Logger* logger_ptr)
2731template<
typename Shm_arena_t>
2741 using boost::this_thread::at_thread_exit;
2745 at_thread_exit([
this]()
2747 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_client: The Gap mutex: locking for this TL-object.");
2753template<
typename Shm_arena_t>
2756 using flow::util::Lock_guard;
2757 using flow::util::Mutex_recursive;
2758 using std::optional;
2760 const auto this_thread_unique_token = flow::util::this_thread_unique_token();
2761 if (this_thread_unique_token != m_thread_token)
2763 FLOW_LOG_WARNING_LOCKED(
"Tl_obj_db_client: "
2764 "Shutting down from different thread (unique-token [" << this_thread_unique_token <<
"]) "
2765 "than the one (unique-token [" << m_thread_token <<
"]) that created us; "
2766 "bailing out. Honestly this is strange; we are a `static` TL-registry, and per-thread "
2767 "dtors should run before the central static object's. In any case this can conceivably -- "
2768 "in the absence of bugs -- only occur when program is exiting, at which "
2769 "point graceful shutdown of resources is arguably less essential.");
2770 assert((!m_locked_gap_mutex) &&
"Tl_obj_db_client: We have a misdesign/bug; something is off thread-wise.");
2781 optional<Lock_guard<Mutex_recursive>> gap_lock;
2782 if (m_locked_gap_mutex)
2784 FLOW_LOG_TRACE_LOCKED(
"Tl_obj_db_client: The Gap mutex is locked: adopting until dtor returns.");
2789 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_client: The Gap mutex is *not* locked; presumably this is the OS/main thread; "
2790 "probably application exit()ing/typically post-main(); no real danger of concurrent "
2800 if_requested_forget_arena_related_resources(
true);
2806 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_client: Thread exiting; will simply close any lend-tracker SHM-pool client handles "
2811 if (!m_per_arena_stats_shards.empty())
2847 for (
const auto& [collection_id, stats_shard] : m_per_arena_stats_shards)
2856template<
typename Shm_arena_t>
2860 return s_state.m_obj_db_registry.this_thread_state();
2863template<
typename Shm_arena_t>
2866 s_state.m_obj_db_registry.set_logger(logger_ptr);
2869template<
typename Shm_arena_t>
2878 m_skip_fast_path_verbose_logging.store(!(logger_ptr
2879 && logger_ptr->should_log(flow::log::Sev::S_TRACE, Log_component::S_SHM)),
2880 std::memory_order_relaxed);
2882 return prev_logger_ptr;
2885template<
typename Shm_arena_t>
2889 using flow::util::stat::fetch_add;
2890 using flow::util::stat::fetch_sub;
2894 const auto it = m_lend_tracker_pools.find(lend_tracker_pool_id);
2895 const auto pool_data = (it == m_lend_tracker_pools.end())
2901 pool_data->m_lend_tracker_pool.use_count_dec(use_ct_idx);
2906 auto& owner_obj_stats = stats_shard(shm_arena)->m_owner_obj;
2907 fetch_add(&owner_obj_stats.m_disposer_count, 1);
2908 fetch_sub(&owner_obj_stats.m_live_handle_groups, 1);
2934template<
typename Shm_arena_t>
2939 using flow::error::Runtime_error;
2945 const auto it = m_lend_tracker_pools.find(lend_tracker_pool_id);
2946 if (it == m_lend_tracker_pools.end())
2957 pool_data = new_pool_data(lend_tracker_pool_id, owner_id, collection_id,
2958 Borrower_shm_pool_collection_repository<Shm_arena>::get_instance()
2959 .recompute_pool_name(owner_id, collection_id, lend_tracker_pool_id));
2962 catch (
const Runtime_error& exc)
2964 if (exc.code() == boost::system::errc::no_such_file_or_directory)
2966 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_client: Borrower-side: In this thread (process ID "
2968 "of borrowed handle, discovered that this thread has not yet accessed the lend-tracker "
2969 "pool from the owner-side thread that constructed handle/object (normal); but "
2970 "that lend-tracker-pool has been removed from the file-system; only the owner side "
2971 "can do this, and only after all objects (including therefore ours) tracked by the "
2972 "lend-tracker-pool have been freed. Probably: user is currently reacting to end of "
2973 "session (connection to opposing process, caused by them, detected by our process) by "
2974 "nullifying (forgetting) all handles originating in arena(s) lent through the "
2975 "now-ended session. In that (probable) case, it is not an error/is not a bad state, "
2976 "and the tracked-object is already freed (or being freed) owner-side, so we need not "
2977 "do anything and can safely no-op. (If something bad *did* happen, this no-op at worst "
2978 "chooses not to explode, when it could; but it is likely something closer to the "
2979 "source of the hypothetical bad state will soon explode more usefully.) "
2980 "Lend-tracker has pool ID [" << lend_tracker_pool_id <<
"] "
2981 "pertaining to arena/collection [" << owner_id <<
'/' << collection_id <<
"]; "
2982 "object use-count index [" << use_ct_idx <<
"].");
3009 pool_data = it->second.get();
3020template<
typename Shm_arena_t>
3024 using flow::util::stat::fetch_add;
3026 const auto it = m_lend_tracker_pools.find(lend_tracker_pool_id);
3027 const auto pool_data = (it == m_lend_tracker_pools.end())
3033 pool_data->m_lend_tracker_pool.use_count_inc(use_ct_idx);
3036 fetch_add(&(stats_shard(shm_arena)->m_lend_obj.m_lend_count), 1);
3048template<
typename Shm_arena_t>
3054 using flow::util::stat::fetch_add;
3055 using flow::util::stat::fetch_sub;
3056 using flow::util::stat::update_hi_wmark;
3057 using std::remove_reference_t;
3098 size_t stat_reap_ct;
3099 size_t stat_reap_check_ct;
3102 vector<pool_id_t> dead_lend_tracker_pool_ids;
3103 for (
const auto& dead_pool_id_and_data : m_lend_tracker_pools)
3106 const auto dead_lend_tracker_pool_id = dead_pool_id_and_data.first;
3107 const auto& pool_data = dead_pool_id_and_data.second;
3109 if (pool_data->m_lend_tracker_pool.dead())
3111 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_client: Opportunistic scan: In this thread (process ID "
3113 "owner's per-constructing-thread's now-dead lend-tracker pool "
3114 "(ID [" << dead_lend_tracker_pool_id <<
"]) "
3115 "tracking objects in arena/collection "
3116 "[" << pool_data->m_uniq_collection_id.first
3117 <<
'/' << pool_data->m_uniq_collection_id.second <<
"]; "
3118 "we are either a borrower process or different thread in owner's process. "
3119 "Owner constructing-thread had marked underlying tracker-pool as dead; presumably either "
3120 "because thread (or degraded admin-thread) exited with no live objects remaining; or "
3121 "because arena was destroyed wholesale (with or without live objects remaining).");
3122 dead_lend_tracker_pool_ids.push_back(dead_lend_tracker_pool_id);
3127 stat_reap_check_ct = m_lend_tracker_pools.size();
3128 stat_reap_ct = dead_lend_tracker_pool_ids.size();
3132 for (
const auto dead_lend_tracker_pool_id : dead_lend_tracker_pool_ids)
3134 m_lend_tracker_pools.erase(dead_lend_tracker_pool_id);
3149 if_requested_forget_arena_related_resources();
3157 const auto update_stats = [&]()
3159 auto& stats = *(obj_db_aux_pool_global_stats_mutable());
3160 const auto pool_opened_else_failed = bool(pool_data_or_none);
3162 if (pool_opened_else_failed)
3165 fetch_add(&stats.m_client_tl_aux_pool_hndl_open_count, 1);
3169 if (stat_reap_check_ct != 0)
3171 fetch_add(&stats.m_client_tl_aux_pool_hndl_reap_check_count, stat_reap_check_ct);
3172 (stat_reap_ct == 0) || fetch_add(&stats.m_client_tl_aux_pool_hndl_reap_count, stat_reap_ct);
3175 if (pool_opened_else_failed)
3178 if (stat_reap_ct == 0)
3180 update_hi_wmark(&stats.m_client_tl_aux_pool_live_hndls_hi_wmark,
3181 fetch_add(&stats.m_client_tl_aux_pool_live_hndls, 1) + 1);
3183 else if (stat_reap_ct != 1)
3185 fetch_sub(&stats.m_client_tl_aux_pool_live_hndls, stat_reap_ct - 1);
3193 if (stat_reap_ct != 0)
3195 fetch_sub(&stats.m_client_tl_aux_pool_live_hndls, stat_reap_ct);
3202 fetch_add(&stats.m_client_tl_aux_pool_hndl_open_fail_count, 1);
3206 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_client: In this thread (process ID "
3208 "owner's per-constructing-thread's lend-tracker pool (ID [" << lend_tracker_pool_id <<
"]) "
3209 "tracking objects in arena/collection "
3210 "[" << owner_id <<
'/' << collection_id <<
"]; "
3211 "we are either a borrower process or different thread in owner's process.");
3217 {
this, &m_skip_fast_path_verbose_logging,
3231 auto& pool_data = m_lend_tracker_pools[lend_tracker_pool_id];
3232 assert((!pool_data) &&
"Breaking contract of this helper internal method.");
3233 pool_data = std::move(pool_data_or_none);
3234 assert(pool_data &&
"Logic bug above? No exception must mean successful pool-opening and non-null Pool_data.");
3236 return pool_data.get();
3240template<
typename Shm_arena_t>
3244 using flow::util::Lock_guard;
3251 const auto collection_id = shm_arena.get_id();
3253 const auto it_collection_id_and_stats = m_per_arena_stats_shards.find(collection_id);
3254 if (it_collection_id_and_stats == m_per_arena_stats_shards.end())
3261 Lock_guard<
decltype(m_per_arena_stats_shards_mutex)> lock{m_per_arena_stats_shards_mutex};
3262 m_per_arena_stats_shards[collection_id].reset(shard);
3270 return it_collection_id_and_stats->second.get();
3273template<
typename Shm_arena_t>
3276 namespace this_thread = flow::util::this_thread;
3293 s_state.m_obj_db_registry.while_locked([&](
const auto& obj_db_per_thread)
3295 if (obj_db_per_thread.empty())
3301 s_state.m_arenas_to_forget_map.while_locked([&](
auto* arenas_to_forget_map)
3303 auto& db_set = (*arenas_to_forget_map)[collection_id];
3305 assert(db_set.empty()
3306 &&
"forgetting_shm_arena() called twice on the same owner+collection ID? Bug?");
3308 for (const auto& [obj_db, nil] : obj_db_per_thread)
3310 db_set.insert(obj_db);
3316 for (
const auto& [obj_db, nil] : obj_db_per_thread)
3318 obj_db->m_forget_resources_requested_poll_flag.arm_next_poll();
3341template<
typename Shm_arena_t>
3344 using flow::util::Lock_guard;
3351 if ((!assume_requested) && (!m_forget_resources_requested_poll_flag.poll_armed()))
3360 s_state.m_arenas_to_forget_map.while_locked([&](
auto* arenas_to_forget_map_ptr)
3362 auto& arenas_to_forget_map = *arenas_to_forget_map_ptr;
3363 vector<collection_id_t> finished_collection_ids;
3365 for (
auto& collection_id_and_db_set : arenas_to_forget_map)
3368 const auto collection_id = collection_id_and_db_set.first;
3369 auto& db_set = collection_id_and_db_set.second;
3371 if (db_set.erase(
this) == 0)
3377 const bool last_one = db_set.empty();
3388 Lock_guard<decltype(m_per_arena_stats_shards_mutex)> lock{m_per_arena_stats_shards_mutex};
3389 erased = (m_per_arena_stats_shards.erase(collection_id) == 1);
3393 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_client: Local arena/collection [" << collection_id <<
"] is being "
3394 "forgotten; so will forget related tracking structures in heap; are we the last "
3395 "per-thread obj-DB that was remaining to do this? = [" << last_one <<
"].");
3401 FLOW_LOG_INFO_LOCKED(
"Tl_obj_db_client: Local arena/collection [" << collection_id <<
"] is being "
3402 "forgotten; opportunistically scanned our per-thread in-heap structures, and this was "
3403 "the last such per-thread structure that needed handling; so this completes the "
3404 "forgetting of that arena/collection among the obj-DB per-thread clients.");
3405 finished_collection_ids.push_back(collection_id);
3410 for (
const auto collection_id : finished_collection_ids)
3412 arenas_to_forget_map.erase(collection_id);
3417template<
typename Shm_arena_t>
3422 using flow::util::stat::Global_stats;
3427 return Global_stats<Thread_lcl_obj_db_client, Obj_db_aux_pool_global_stats>::get().stats_default();
3430template<
typename Shm_arena_t>
3436 return &(flow::util::stat::Global_stats<Thread_lcl_obj_db_client, Obj_db_aux_pool_global_stats>::get()
3437 .stats_mutable_default());
3440template<
typename Shm_arena_t>
3445 flow::util::stat::stats_reset(obj_db_aux_pool_global_stats_mutable(), Obj_db_aux_pool_global_stats{});
3448template<
typename Shm_arena_t>
3450 m_obj_db_registry(nullptr,
"Thread_lcl_obj_db_client")
3452 Set_logger_registry::register_action([](flow::log::Logger* logger_ptr)
Singleton repository for borrowed SHM-pool data.
This internal-use class represents, essentially, a handle to the use-count database (which the class ...
unsigned int use_count_dec(use_ct_idx_t use_ct_idx)
Atomically decrements the use-count at the given slot; for use in client mode (m_is_creator is false)...
static pool_id_t generate_pool_id()
Returns a heretofore-unused pool ID so as to be used to identify (in a Shm_pool_offset_ptr_data::Repo...
(Internal-use) Thread_lcl_obj_db_admin<A>::this_thread_obj_db() is the current thread's module that t...
static Thread_lcl_obj_db_admin * this_thread_obj_db()
Returns the calling thread's Thread_lcl_obj_db_admin, creating it on first call from that thread.
void if_requested_forget_arena_related_resources(bool assume_requested=false)
If m_forget_resources_requested_poll_flag has been armed (by forgetting_shm_arena() from another thre...
void forget_shm_arena(collection_id_t collection_id, bool last_one)
Performs the actual per-arena cleanup for a single arena being forgotten: destroys all remaining live...
static bool forgetting_shm_arena(collection_id_t collection_id, flow::log::Logger *logger_ptr, On_done_func &&on_done_func)
Initiates the process of forgetting (cleaning up) a given Shm_arena across all per-thread Thread_lcl_...
static void atexit_degraded_admin_threads_join()
std::atexit() handler: joins any degraded-admin threads that are still running at program exit.
boost::unordered_flat_map< collection_id_t, Own< Collection_db > > m_collection_dbs
Per-arena tracking map: keyed by collection-ID, each value is the arena's Collection_db containing th...
boost::movelib::unique_ptr< T > Own
Short-hand for single-ownership pointer (unique_ptr of some sort).
~Thread_lcl_obj_db_admin()
Destroys this per-thread admin.
void disposing_obj(collection_id_t collection_id, use_ct_idx_t use_ct_idx)
Records disposal of a handle to a constructed object (as of this writing: shared_ptr group reaching r...
stat::Sharded_stats * stats_update_pre_consumption(collection_id_t collection_id)
Intended for use during stat-consumption or stat-reset, updates certain values (namely some GAUGEs) o...
void constructing_obj(pool_id_t *lend_tracker_pool_id, use_ct_idx_t *use_ct_idx, Shm_arena *shm_arena, arena_lend::stat::Obj_db_aux_pool_stats *obj_db_aux_pool_stats, void *addr, Deleter_func_obj &&del_func)
Records a newly construct<T>(...)ed object.
static void dbs_set_logger(flow::log::Logger *logger_ptr)
Propagates the given logger to the extant (and future) per-thread Thread_lcl_obj_db_admin objects (vi...
Shm_arena_t Shm_arena
Short-hand for template parameter type. See our class doc header for requirements.
void lending_obj(collection_id_t collection_id, use_ct_idx_t use_ct_idx)
Records that a constructed object is being lent (IPC-transmitted) to another process.
bool inactive_arenas_scan()
Post-scan scan and cleanup for a thread that is exiting (or in degraded-admin mode): for each arena w...
bool m_locked_gap_mutex
Helper state for the stat::thread_end_gap_mutex() mechanism: Starts false; becomes true permanently j...
static Static_state s_state
The singleton shared state for all per-thread admins of this Shm_arena type.
Thread_lcl_obj_db_admin(flow::log::Logger *logger_ptr)
Constructs thread-local *this as invoked internally by Static_state::m_obj_db_registry (on first this...
flow::util::Poll_flag m_forget_resources_requested_poll_flag
Per-thread poll-flag for the Polled_shared_state pattern on Static_state::m_arenas_to_forget_map.
static void this_thread_piggy_scan()
If the calling thread has a Thread_lcl_obj_db_admin (i.e., this_thread_obj_db() has been called at le...
flow::util::Mutex_non_recursive m_collection_dbs_mutex
Protects map m_collection_dbs; but don't always lock when accessing map; see m_collection_dbs doc hea...
bool skip_fast_path_verbose_logging() const
Reads m_skip_fast_path_verbose_logging.
Function< void(void *, Shm_arena *)> Deleter_func
Concrete type for how we store Deleter_func_obj functors supplied in constructing_obj().
std::atomic< bool > m_skip_fast_path_verbose_logging
Iff true, TRACE-or-more-verbose logging was disabled at the last set_logger() call; fast-path logging...
void degraded_admin_thread_body(Thread_lcl_obj_db_admin *exiting_thread_obj_db, boost::promise< void > *done_with_exiting_thread_obj_db)
Body of a degraded-admin thread: periodically (every ~100ms as of this writing) performs unused_obj_s...
void unused_obj_scan(bool exhaustive, bool draining)
Core of the piggy-scan (+ certain special-case triggers): for each arena tracked by *this,...
flow::log::Logger * set_logger(flow::log::Logger *logger_ptr)
Sets (replaces) the logger used by *this.
const flow::util::Thread_token m_thread_token
The (unique across time, within this process) ID of the thread that owns *this (the thread from which...
(Internal-use) Where a Thread_lcl_obj_db_admin cannot handle lend-object and dispose-object tracking,...
flow::util::Mutex_non_recursive m_per_arena_stats_shards_mutex
Protects map m_per_arena_stats_shards but see its doc header regarding when the locking is necessary.
static const arena_lend::stat::Obj_db_aux_pool_global_stats & obj_db_aux_pool_global_stats()
Returns reference-to-const stat-set, carrying information that is global but per-template-instance-ty...
Thread_lcl_obj_db_client(flow::log::Logger *logger_ptr)
Constructs thread-local *this as invoked internally by Static_state::m_obj_db_registry (on first this...
flow::log::Logger * set_logger(flow::log::Logger *logger_ptr)
Identical to Thread_lcl_obj_db_admin::set_logger().
static void dbs_set_logger(flow::log::Logger *logger_ptr)
Identical to Thread_lcl_obj_db_admin::dbs_set_logger().
boost::movelib::unique_ptr< T > Own
Short-hand for Thread_lcl_obj_db_admin::Own.
boost::unordered_flat_map< pool_id_t, Own< Pool_data > > m_lend_tracker_pools
Map from lend-tracker-pool ID to its client-side bookkeeping.
void if_requested_forget_arena_related_resources(bool assume_requested=false)
If m_forget_resources_requested_poll_flag has been armed (by forgetting_shm_arena() from potentially ...
void lending_obj(const Shm_arena &shm_arena, pool_id_t lend_tracker_pool_id, use_ct_idx_t use_ct_idx)
Client-side (owner process, non-constructing thread) counterpart to Thread_lcl_obj_db_admin::lending_...
boost::unordered_flat_map< collection_id_t, Own< stat::Sharded_stats > > m_per_arena_stats_shards
For each arena for which 1+ lending_obj() or (owner-process overload) disposing_obj() has been perfor...
~Thread_lcl_obj_db_client()
Closes any client-mode Lend_tracker_pool handles we hold; thread is exiting so they won't be needed.
bool m_locked_gap_mutex
Identical to Thread_lcl_obj_db_admin::m_locked_gap_mutex. It is a bit hairy so do read that doc heade...
stat::Sharded_stats * stats_shard(const Shm_arena &shm_arena)
Helper that returns the TL-shard m_per_arena_stats_shards[C], where C identifies *shm_arena,...
const flow::util::Thread_token m_thread_token
Analogous to Thread_lcl_obj_db_admin::m_thread_token.
void disposing_obj(const Shm_arena &shm_arena, pool_id_t lend_tracker_pool_id, use_ct_idx_t use_ct_idx)
Client-side (owner process, non-constructing thread) counterpart to Thread_lcl_obj_db_admin::disposin...
Pool_data * new_pool_data(pool_id_t lend_tracker_pool_id, owner_id_t owner_id, collection_id_t collection_id, const Shared_name &new_pool_name)
Adds a new entry to m_lend_tracker_pools for the given pool ID (opening a client-mode Lend_tracker_po...
static Static_state s_state
The singleton shared state for all per-thread clients of this Shm_arena type.
static void forgetting_shm_arena(collection_id_t collection_id)
Initiates the process of forgetting (cleaning up) a given Shm_arena across all per-thread Thread_lcl_...
static Thread_lcl_obj_db_client * this_thread_obj_db()
Identical to Thread_lcl_obj_db_admin::this_thread_obj_db() but for the client-side DB.
flow::util::Poll_flag m_forget_resources_requested_poll_flag
Analogous to eponymous Thread_lcl_obj_db_admin::m_forget_resources_requested_poll_flag.
std::atomic< bool > m_skip_fast_path_verbose_logging
Analogous to Thread_lcl_obj_db_admin::m_skip_fast_path_verbose_logging.
Shm_arena_t Shm_arena
Short-hand alias for the template parameter.
static arena_lend::stat::Obj_db_aux_pool_global_stats * obj_db_aux_pool_global_stats_mutable()
Mutable version of obj_db_aux_pool_global_stats(), for internal stat-updating.
static void obj_db_aux_pool_global_stats_reset()
Resets obj_db_aux_pool_global_stats(). Formal meaning of a reset is discussed in flow::util::stat doc...
(Internal-use) Singleton (per-Shm_arena type <=> per-arena-lending SHM-provider – like SHM-jemalloc) ...
void stats_insert(collection_id_t collection_id, const Sharded_stats &stats_shard)
Folds (field-wise-sums, a-la flow::util::stat::stats_aggregate_one()) the given stats shard into the ...
static Finalized_shards & get_instance()
Returns the process-wide singleton instance of this class.
static process_id_t own_process_id()
Obtains the calling process's process_id().
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
flow::util::Mutex_recursive & thread_end_gap_mutex()
(Helper for sharded_stats_impl() and Thread_lcl_obj_db_admin/client) Process-global,...
void sharded_stats_impl(const Shm_arena &shm_arena, Sharded_stats *target_stats)
Helper: implements sharded_stats() or sharded_stats_reset().
arena_lend::stat::Sharded_stats Sharded_stats
Short-hand for brevity: Thread_lcl_obj_db_* and buddies make frequent mention of this.
Segregated private stuff for ipc::shm::arena_lend.
uint32_t pool_id_t
Pool ID type.
uint32_t use_ct_idx_t
Index into a given Lend_tracker_pool's bulk storage of arena-construct()ed (outer a/k/a first-class) ...
std::pair< owner_id_t, collection_id_t > Uniq_collection_id
Globally unique (up to reboot) arena (collection) ID.
void set_logger(flow::log::Logger *logger_ptr)
Convenience function that sets (or clears) the Logger used by all SHM-arena-lend singleton subsystems...
uint32_t collection_id_t
Identifier type for a shared memory pool collection.
util::process_id_t owner_id_t
Alias for an identifier of the owner (essentially namespace of collection_id_ts) of shared informatio...
const Open_only OPEN_ONLY
Tag value indicating an atomic open-if-exists-else-fail operation.
flow::Fine_duration Fine_duration
Short-hand for Flow's Fine_duration.
const Create_only CREATE_ONLY
Tag value indicating an atomic create-unless-exists-else-fail operation.
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
flow::Function< Signature > Function
Short-hand for polymorphic functor holder which is very common. This is essentially std::function.
This is the friend facade of Owner_shm_pool_collection that exposes non-public APIs hidden from publi...
void this_thread_ensure_tcache_exists() const
See eponymous method in Base.
Shared_name generate_shm_object_name(Owner_shm_pool_collection::pool_id_t shm_pool_id) const
See Owner_shm_pool_collection::generate_shm_object_name().
For each arena currently being forgotten via forgetting_shm_arena(), the state tracking the progress ...
Function< void(const flow::log::Log_context_mt *)> m_on_done_func
Functor to invoke, and then immediately destroy, once m_dbs_that_still_must_forget is ....
boost::unordered_flat_set< Thread_lcl_obj_db_admin * > m_dbs_that_still_must_forget
Which Thread_lcl_obj_db_admin extant objects have yet to invoke if_requested_forget_arena_related_res...
A single constructed object's tracking data: its address and the functor to destroy/dealloc it.
void * m_addr
Process-local address of the constructed object (as returned by construct<T>()).
Deleter_func m_del_func
Functor invoked as m_del_func(m_addr, Collection_db::m_shm_arena) to destroy the object.
Per-arena (per-collection-ID) tracking state owned by this per-thread admin.
std::optional< Lend_tracker_pool > m_lend_tracker_pool
Admin-mode Lend_tracker_pool for use-count tracking of objects constructed in this arena + cting thre...
flow::util::Linked_hash_map< use_ct_idx_t, Object > m_objs
Map from use-count-slot index to Object, in insertion (construction) order.
Shm_arena * m_shm_arena
The arena that owns the SHM backing for objects tracked by this Collection_db.
stat::Sharded_stats m_stats_shard
For *this arena, for the containing-*this-constructing thread, the stat-members (all atomic) directly...
pool_id_t m_lend_tracker_pool_id
Pool-ID of m_lend_tracker_pool; generated once on first construct from this arena + cting thread.
Singleton state shared across all per-thread Thread_lcl_obj_db_admin instances (for a given Shm_arena...
flow::util::Mutex_non_recursive m_degraded_admin_threads_mutex
Protects m_degraded_admin_threads.
flow::util::Polled_shared_state< boost::unordered_flat_map< collection_id_t, Arena_forget_progress > > m_arenas_to_forget_map
State shared – and acted upon using the Polled_shared_state pattern – between extant Thread_lcl_obj_d...
Static_state()
Constructor.
std::vector< Own< flow::util::Thread > > m_degraded_admin_threads
Degraded-admin threads spawned by dtors of Thread_lcl_obj_db_admin objects whose owning threads exite...
flow::util::Thread_local_state_registry< Thread_lcl_obj_db_admin > m_obj_db_registry
Registry of all extant per-thread Thread_lcl_obj_db_admin objects; manages their lifecycle and logger...
Per-lend-tracker-pool bookkeeping on the client side: the pool handle itself and its identity.
Lend_tracker_pool m_lend_tracker_pool
Client-mode handle to the SHM-pool containing use-count slots.
Uniq_collection_id m_uniq_collection_id
Identifies the arena/collection this pool belongs to (owner PID + collection ID). For logging.
Singleton state shared across all per-thread Thread_lcl_obj_db_client instances (for a given Shm_aren...
flow::util::Polled_shared_state< boost::unordered_flat_map< collection_id_t, boost::unordered_flat_set< Thread_lcl_obj_db_client * > > > m_arenas_to_forget_map
State shared – and acted upon using the Polled_shared_state pattern – between extant Thread_lcl_obj_d...
Static_state()
Constructor.
flow::util::Thread_local_state_registry< Thread_lcl_obj_db_client > m_obj_db_registry
Registry of all extant per-thread Thread_lcl_obj_db_client objects; manages their lifecycle and logge...
Similar mission to Obj_db_aux_pool_stats but covers information that, by nature, is not per-arena but...
Stats about the RAM use of the arena-lending SHM-provider's auxiliary (non-user-data) SHM-pools which...
A bundle of several individually-documented stat-sets – object construct/dispose/live and zombie-reap...
shm::stat::Owner_obj_stats m_owner_obj
Sub-struct with individual stats; see type's doc header.
Cousin of arena_lend::stat::Owner_obj_stats, this tracks the zombie-reaper scan algorithm whose task ...