32#include <flow/util/util.hpp>
40 const std::atomic<bool>* skip_fast_path_verbose_logging_ptr,
44 m_pool_name(pool_name),
46 m_skip_fast_path_verbose_logging_ptr(skip_fast_path_verbose_logging_ptr),
50 using flow::util::stat::fetch_add;
51 using flow::util::stat::update_hi_wmark;
53 using boost::io::ios_all_saver;
59 const auto logger_ptr = get_logger();
60 if (logger_ptr && logger_ptr->should_log(flow::log::Sev::S_INFO, get_log_component()))
62 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
63 FLOW_LOG_INFO_WITHOUT_CHECKING
64 (
"Lend-tracker pool (addr [" <<
this <<
"]): "
65 "Constructing heap handle to tracker pool at name [" <<
m_pool_name <<
"] in "
66 "create-only (admin) mode; pool size [" << flow::util::ceil_div(POOL_SZ,
size_t(1024)) <<
"Ki]; "
67 "perms = [" << std::setfill(
'0') << std::setw(4) << std::oct << perms.get_permissions() <<
"]. "
68 "Then will initialize state.");
79 m_pool->close_shm_object_handle();
96 for (
auto hint = hints.begin(); hint != hints.end(); ++hint)
98 flow::util::construct_at(hint, 0);
102 &&
"By definition this should be that: we just made it!");
106 const std::atomic<bool>* skip_fast_path_verbose_logging_ptr,
109 m_pool_name(pool_name),
111 m_skip_fast_path_verbose_logging_ptr(skip_fast_path_verbose_logging_ptr),
121 m_pool->close_shm_object_handle();
127 (
"Lend-tracker pool [" << *
this <<
"]: "
128 "Constructed heap handle to tracker pool at name [" <<
m_pool_name <<
"] in open-only (client) mode.");
134 using flow::util::stat::fetch_sub;
139 (
"Lend-tracker pool [" << *
this <<
"]: Creating/admin pool (essentially pool handle) shutting down. This "
140 "indicates no tracked-objects relevant to this pool will ever be allocated or deallocated. Therefore "
141 "(1) setting the DEAD flag to true in pool; and (2) "
142 "removing pool-name [" <<
m_pool_name <<
"] from file-system (any already-opened handles will not be "
143 "disrupted, though for unrelated reasons they too should be shutting down or already shut-down).");
162 m_pool->core()->get_segment_manager()->get_memory_algorithm().stats_record_at_deletion(
m_stats);
168 (
"Lend-tracker pool [" << *
this <<
"]: Non-creating/admin pool (essentially pool handle) shutting down.");
171 &&
"As of this writing the convention is that client-mode Lend_tracker_pool keeps no stats. "
172 "Maintenance bug in Lend_tracker_pool?");
185 using std::exception;
192 use_ct_ptr =
static_cast<decltype(use_ct_ptr)
>(
pool_allocate(
sizeof(*use_ct_ptr)));
194 catch (
const exception& exc)
196 FLOW_LOG_WARNING_LOCKED
197 (
"Lend-tracker pool [" << *
this <<
"]: "
198 "Unable to obtain a new use-count slot; appears a thread created a huge # of live objects at "
199 "the same time; we cannot handle this. Our code may need to change to allow for more slots "
200 "at the cost of somewhat higher mandatory aux RAM use. Will re-throw likely bad_alloc: "
201 "[" << exc.what() <<
"].");
204 assert(use_ct_ptr &&
"allocate() would have thrown (with WARNING logged), else should have returned non-null.");
206 flow::util::construct_at(use_ct_ptr, 1);
211 FLOW_LOG_TRACE_LOCKED(
"Lend-tracker pool [" << *
this <<
"]: "
212 "New object presumably constructed: new use-count `1` registered at idx [" << idx <<
"], "
213 "process-local addr [" << use_ct_ptr <<
"] off base addr [" <<
m_metadata <<
"].");
223 auto use_ct =
static_cast<unsigned int>(use_ct_ptr->fetch_add(1, std::memory_order_relaxed));
226 &&
"Use-count starts at 1; and on reaching 0 must never be touched again. Bug?");
229 constexpr auto MAX_USE_CT = std::numeric_limits<Atomic_use_ct::value_type>::max();
230 if (use_ct == MAX_USE_CT)
232 FLOW_LOG_FATAL_LOCKED(
"Lend-tracker pool [" << *
this <<
"]: "
233 "Object use-count INCremented from [" << use_ct <<
"] at idx [" << use_ct_idx <<
"], "
234 "process-local addr [" << use_ct_ptr <<
"] off base addr [" <<
m_metadata <<
"]; "
235 "overflow! This means you've lent -- and held simultaneously without dropping by receiving "
236 "process -- this object that many times, plus one. Either reconsider your use case (are you "
237 "lending many times to one process? if so can you detect this and simply copy the shared_ptr "
238 "instead?); or modify Use_count_registry::S_ALLOC_SZ and Lend_tracker_pool::Atomic_use_ct "
239 "definitions to handle even more lending. Aborting.");
240 assert(
false &&
"Object use-count INCremented from the max-value of Atomic_use_ct::value_type; "
241 "overflow! This means you've lent -- and held simultaneously without dropping by receiving "
242 "process -- this object that many times, plus one. Either reconsider your use case (are you "
243 "lending many times to one process? if so can you detect this and simply copy the shared_ptr "
244 "instead?); or modify Use_count_registry::S_ALLOC_SZ and Lend_tracker_pool::Atomic_use_ct "
245 "definitions to handle even more lending. Aborting.");
254 FLOW_LOG_TRACE_LOCKED(
"Lend-tracker pool [" << *
this <<
"]: "
255 "Object use-count INCremented to [" << use_ct <<
"] at idx [" << use_ct_idx <<
"], "
256 "process-local addr [" << use_ct_ptr <<
"] off base addr [" <<
m_metadata <<
"].");
303 auto use_ct =
static_cast<unsigned int>(use_ct_ptr->fetch_sub(1, std::memory_order_release));
305 assert((use_ct != 0) &&
"Use-count on reaching 0 must never be touched again... yet has become negative. Bug?");
323 found_hint_ptr = find_if(hints.begin(), hints.end(), [&](
auto& hint)
326 if (hint.compare_exchange_strong(to_replace, use_ct_idx, std::memory_order_relaxed))
339 if (!skip_fast_path_verbose_logging())
341 log_while_locked([&](
auto&& get_logger,
auto&& get_log_component)
343 const auto logger_ptr = get_logger();
344 if (logger_ptr && logger_ptr->should_log(flow::log::Sev::S_TRACE, get_log_component()))
346 FLOW_LOG_TRACE_WITHOUT_CHECKING
347 (
"Lend-tracker pool [" << *
this <<
"]: "
348 "Object use-count DECremented to [" << use_ct <<
"] at idx [" << (use_ct_idx - 1) <<
"], "
349 "process-local addr [" << use_ct_ptr <<
"] off base addr [" << m_metadata <<
"]. If "
350 "reached zero will record this in pool metadata area presently.");
353 FLOW_LOG_TRACE_WITHOUT_CHECKING
354 (
"Yes, reached zero: count of all such (unused-object-not-yet-deleted) slots is now INCremented to "
355 "[" << (n_unused + 1) <<
"]; recorded in pool metadata area. Communicated this fact "
356 "via hint array, if possible.");
357 const auto& hints = m_metadata->m_unused_idx_hints;
358 if (found_hint_ptr != hints.end())
360 FLOW_LOG_TRACE_WITHOUT_CHECKING
361 (
"Recorded index of unused-object-not-yet-deleted slot in hint-array slot "
362 "[" << (found_hint_ptr - hints.begin()) <<
"] (0-based) of "
363 "[" <<
decltype(Metadata::m_unused_idx_hints)::static_size <<
"] (1-based).");
374unsigned int Lend_tracker_pool::use_count_dec_admin(
use_ct_idx_t* use_ct_idx_ptr)
378 auto& use_ct_idx = *use_ct_idx_ptr;
379 auto const use_ct_ptr = use_ct_idx_to_ptr(use_ct_idx);
381 auto use_ct =
static_cast<unsigned int>(use_ct_ptr->fetch_sub(1, std::memory_order_release));
383 assert((use_ct != 0) &&
"Use-count on reaching 0 must never be touched again... yet has become negative. Bug?");
387 if (!skip_fast_path_verbose_logging())
389 FLOW_LOG_TRACE_LOCKED
390 (
"Lend-tracker pool [" << *
this <<
"]: "
391 "Object use-count DECremented admin-style to [" << use_ct <<
"] at idx [" << use_ct_idx <<
"], "
392 "process-local addr [" << use_ct_ptr <<
"] off base addr [" << m_metadata <<
"]. If "
393 "reached zero will (admin-style!) return this slot of use-count immediately. "
394 "(If so then presumably caller, as admin, can finish the tracked-object's existence immediately, so "
395 "no need to record this use-ct=0 in pool metadata.)");
401 std::atomic_thread_fence(std::memory_order_acquire);
402 use_count_return_bc_unused(use_ct_idx_ptr,
410bool Lend_tracker_pool::use_count_return_if_unused(
use_ct_idx_t* use_ct_idx_ptr)
412 auto& use_ct_idx = *use_ct_idx_ptr;
413 auto const use_ct_ptr = use_ct_idx_to_ptr(use_ct_idx);
414 if (use_ct_ptr->load(std::memory_order_relaxed) != 0)
416 if (!skip_fast_path_verbose_logging())
418 FLOW_LOG_TRACE_LOCKED(
"Lend-tracker pool [" << *
this <<
"]: Object would be deleted if use-count were 0: "
419 "but it is not; ignoring; registered at idx [" << use_ct_idx <<
"], "
420 "process-local addr [" << use_ct_ptr <<
"] off base addr [" << m_metadata <<
"].");
435 std::atomic_thread_fence(std::memory_order_acquire);
437 use_count_return_bc_unused(use_ct_idx_ptr,
443void Lend_tracker_pool::use_count_return_bc_unused(
use_ct_idx_t* use_ct_idx_ptr,
bool was_not_counted_as_unused)
445 auto& use_ct_idx = *use_ct_idx_ptr;
446 auto const use_ct_ptr = use_ct_idx_to_ptr(use_ct_idx);
448 if (was_not_counted_as_unused)
450 if (!skip_fast_path_verbose_logging())
452 FLOW_LOG_TRACE_LOCKED(
"Lend-tracker pool [" << *
this <<
"]: Object presumably being deleted: "
453 "returning slot of use-count (by contract equal to 0) registered at idx "
454 "[" << use_ct_idx <<
"], process-local addr [" << use_ct_ptr <<
"] off base addr "
455 "[" << m_metadata <<
"]. Admin-style, count of all unused-object-not-yet-deleted slots "
456 "was *neither* incremented nor decremented.");
462 auto n_unused = m_metadata->m_n_unused.fetch_sub(1, std::memory_order_relaxed);
464 if (!skip_fast_path_verbose_logging())
466 FLOW_LOG_TRACE_LOCKED(
"Lend-tracker pool [" << *
this <<
"]: Object presumably being deleted: "
467 "returning slot of use-count (by contract equal to 0) registered at idx "
468 "[" << use_ct_idx <<
"], process-local addr [" << use_ct_ptr <<
"] off base addr "
469 "[" << m_metadata <<
"]. Count of all unused-object-not-yet-deleted slots is now "
470 "DECremented to [" << (n_unused - 1) <<
"]; recorded in pool metadata area.");
476 use_ct_ptr->~Atomic_use_ct();
477 m_pool->core()->deallocate(use_ct_ptr);
482unsigned int Lend_tracker_pool::use_count(
use_ct_idx_t use_ct_idx)
const
484 return use_ct_idx_to_ptr(use_ct_idx)->load(std::memory_order_relaxed);
490 return static_cast<use_ct_idx_t>(
reinterpret_cast<uintptr_t
>(use_ct_ptr)
491 -
reinterpret_cast<uintptr_t
>(m_metadata))
499 (
reinterpret_cast<uintptr_t
>(m_metadata)
500 + (use_ct_idx * USE_CT_SZ));
503signed int Lend_tracker_pool::n_unused()
const
505 return m_metadata->m_n_unused.load(std::memory_order_relaxed);
508void* Lend_tracker_pool::pool_allocate(
size_t sz)
521 auto& mem_algo = m_pool->core()->get_segment_manager()->get_memory_algorithm();
522 const auto qta = mem_algo.stat_quanta_active();
523 const auto ret = m_pool->core()->allocate(sz);
524 mem_algo.stats_record(qta, m_stats);
528 return m_pool->core()->allocate(sz);
532 const std::atomic<bool>* skip_fast_path_verbose_logging_ptr)
535 m_skip_fast_path_verbose_logging_ptr = skip_fast_path_verbose_logging_ptr;
538bool Lend_tracker_pool::skip_fast_path_verbose_logging()
const
540 return m_skip_fast_path_verbose_logging_ptr->load(std::memory_order_relaxed);
548 <<
"[sh_name[" << val.
m_pool_name <<
"] unused-cts[" << val.
n_unused() <<
"] use-cts-used/free"
549 "[" << (((Use_count_registry::S_USE_COUNTS_CAPACITY * USE_CT_SZ) - val.
m_pool->core()->get_free_memory())
552 << (val.
m_pool->core()->get_free_memory() / USE_CT_SZ) <<
"]]@" << &val;
This internal-use class represents, essentially, a handle to the use-count database (which the class ...
use_ct_idx_t use_ct_ptr_to_idx(const Atomic_use_ct *use_ct_ptr) const
The reverse of use_ct_idx_to_ptr(), this converts a process-local vaddr pointing at a use-count slot ...
Lend_tracker_pool(const flow::log::Log_context_mt *log_ctx, const std::atomic< bool > *skip_fast_path_verbose_logging_ptr, const Shared_name &pool_name, util::Create_only mode_tag, Obj_db_aux_pool_stats *stats, const util::Permissions &perms={})
Constructs the admin-mode (creating) handle: creates the SHM-pool named pool_name and initializes its...
const Shared_name m_pool_name
SHM pool name as set immutably at construction.
unsigned int use_count_inc(use_ct_idx_t use_ct_idx)
Atomically increments the use-count at the given slot.
std::optional< Pool > m_pool
Un-nullified permanently in ctor, this is the handle to the high-perf use-count-tracking SHM-pool per...
std::atomic< uint32_t > Atomic_use_ct
The type of value stored in each use-count slot in m_pool.
signed int n_unused() const
Returns the current count of use-count slots that are unused-referencing (use-count has reached zero ...
bool dead() const
Returns false (indicating liveness of the SHM-pool we handle) throughout; until the admin-mode (m_is_...
Obj_db_aux_pool_stats * m_stats
Stats to update in admin-mode; null => skips stat-keeping.
bool skip_fast_path_verbose_logging() const
Reads m_skip_fast_path_verbose_logging_ptr pointee's current value.
void log_while_locked(Logging_task &&logging_task) const
Forwards to m_log_ctx->log_while_locked().
Metadata * m_metadata
Pointer to the Metadata in m_pool; also used as the base for use_ct_idx indices produced or consumed ...
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)...
use_ct_idx_t use_count_new()
Allocates a new use-count slot and initializes its value to 1 (one extant handle: the just-constructe...
~Lend_tracker_pool()
Destroys this handle.
void * pool_allocate(size_t sz)
Forwards to m_pool->allocate(sz) while recording – accurately but performantly – into *m_stats as nee...
const bool m_is_creator
true if admin-mode (create-mode ctor was used); false if client-mode (open-mode ctor was used).
Atomic_use_ct * use_ct_idx_to_ptr(use_ct_idx_t use_ct_idx) const
The reverse of use_ct_ptr_to_idx().
static constexpr size_t base_offset()
The number of bytes between the start of the SHM-pool (segment) storing us and us (*this).
static void remove_persistent(flow::log::Logger *logger_ptr, const Shared_name &name, Error_code *err_code=nullptr)
Removes the named SHM pool object.
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
Segregated private stuff for ipc::shm::arena_lend.
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) ...
@ S_SHM_BIPC_MISC_LIBRARY_ERROR
Low-level boost.ipc.shm: boost.interprocess emitted miscellaneous library exception sans a system cod...
std::ostream & operator<<(std::ostream &os, const Msg_batch_in< Mutable_buffer_sequence_t, Msg_resource_t > &val)
Serializes a Msg_batch_in to a standard output stream.
bipc::permissions Permissions
Short-hand for Unix (POSIX) permissions class.
bipc::open_only_t Open_only
Tag type indicating an ideally-atomic open-if-exists-else-fail operation.
const Open_only OPEN_ONLY
Tag value indicating an atomic open-if-exists-else-fail operation.
bipc::create_only_t Create_only
Tag type indicating a create-unless-exists-else-fail operation.
void op_with_possible_bipc_exception(flow::log::Logger *logger_ptr, Error_code *err_code, const Error_code &misc_bipc_lib_error, String_view context, const Func &func)
Internal (to ipc) utility that invokes the given function that invokes a boost.interprocess operation...
const Create_only CREATE_ONLY
Tag value indicating an atomic create-unless-exists-else-fail operation.
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Stats about the RAM use of the arena-lending SHM-provider's auxiliary (non-user-data) SHM-pools which...