JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
Loading...
Searching...
No Matches
flat_pool.h File Reference

Key-based, contiguous, GC-like interning pool for copyable or movable objects. More...

#include <compare>
#include <vector>
#include <shared_mutex>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <tuple>
#include <memory>
#include <mutex>
#include <algorithm>
#include <type_traits>
#include <stdexcept>
#include "jh/typing/monostate.h"
#include "jh/core/ordered_map.h"
#include "jh/conceptual/hashable.h"
#include "jh/synchronous/control_buf.h"
#include "jh/synchronous/strong_lock.h"

Go to the source code of this file.

Classes

struct  jh::conc::extension::value_factory< V >
 Default value construction policy for flat_pool. More...
struct  jh::conc::extension::value_factory< std::shared_ptr< T > >
 Value construction policy specialization for std::shared_ptr. More...
struct  jh::conc::extension::value_factory< std::unique_ptr< T > >
 Value construction policy specialization for std::unique_ptr. More...
class  jh::conc::flat_pool< Key, Value, Hash, Alloc >
 Hash-ordered, contiguous resource interning pool. More...
struct  jh::conc::flat_pool< Key, Value, Hash, Alloc >::ptr
 Reference-counted handle to a pooled object. More...

Namespaces

namespace  jh::conc
 Aggregated namespace for concurrency-aware resource containers.
namespace  jh::conc::extension
 Public customization and extension points for jh::conc containers.

Detailed Description

Key-based, contiguous, GC-like interning pool for copyable or movable objects.

Author
JeongHan-Bae <mastropseudo@gmail.com>

Overview

jh::conc::flat_pool is a concurrent object interning container that deduplicates objects using an explicit external key and stores them inside a contiguous memory pool. Each unique key corresponds to at most one active slot at any time, and acquisitions return lightweight reference-counted handles (flat_pool::ptr) bound to stable indices.

Key-Based Identity Model

Object identity is defined entirely by an external Key type. The key must be:

  • lightweight to construct,
  • cheap to hash and compare,
  • capable of representing object identity independently of object storage.

Lookup and deduplication are performed solely through the key. The stored object itself is never inspected for equality. This allows the pool to perform lookups before construction and avoids provisional object creation.

Keys are accepted in cv/ref-qualified form during acquisition. These equivalent key representations are used only for identification and are not retained unless insertion succeeds.

Value Construction Model

For map-like pools (Value != jh::typed::monostate), values are constructed using a std::tuple of arguments supplied at acquisition time.

  • The argument tuple is treated as initialization-only data.
  • The argument tuple is not a complete data-tuple, but rather a left-value-tuple or perfectly-forwarded-tuple bound via std::tie or std::forward_as_tuple.
  • If an equivalent key already exists, the tuple is discarded without constructing a value.
  • Value construction occurs exactly once per unique key.

Value creation is customizable via the public extension point jh::conc::extension::value_factory<Value>, enabling user-defined construction strategies without modifying or subclassing the pool.

GC-like Lifetime Semantics

flat_pool deliberately adopts a GC-like lifetime model:

  • Reference counting represents liveness, not destruction.
  • When a slot's reference count drops to zero, the slot becomes reusable.
  • Objects are not destroyed immediately when they become unused.

Instead of invoking destructors eagerly, the pool prefers slot reuse through assignment. This avoids repeated destructor / constructor cycles and significantly reduces allocation pressure for objects whose initialization may involve expensive memory allocation or resource setup.

Because destruction is deferred and non-deterministic, flat_pool is not suitable for objects whose correctness depends on immediate destruction when references are released.

In a manner of speaking, this design can be viewed as transforming shared_ptr, which performs allocation individually, into a bunch of smart pointers that allocate keys, values, index tables, and control blocks all in contiguous memory managed by the pool.

Concurrency Model

All synchronization is handled internally by the pool using shared and exclusive locks. No external atomic or smart-pointer-level synchronization is relied upon.

  • Lookup operations acquire shared locks only.
  • Insertion, release, and resizing acquire exclusive locks.
  • Reference counts are maintained using atomics.

The pool stores objects in contiguous memory. To protect against vector reallocation during concurrent access, dereferencing a handle in multithreaded contexts requires holding a no_reallocate_guard.

Comparison with pointer_pool

flat_pool and pointer_pool address complementary problem domains:

  • flat_pool:
    • Key-driven identity
    • Contiguous storage
    • Slot reuse via assignment
    • GC-like deferred destruction
    • Entirely pool-controlled synchronization
  • pointer_pool (observe_pool):
    • Pointer-driven identity (Generally, comparisons of internal objects are proxied using jh::weak_ptr_hash and jh::weak_ptr_eq.)
      On Windows, the native implementation of shared_ptr can cause greater jitter than POSIX.
    • Heap-allocated, immovable objects
    • Immediate destruction via shared_ptr
    • Weak-observed lifetime

If your objects:

  • are copyable or movable,
  • can be identified by a lightweight external key,
  • benefit from slot reuse and delayed destruction,

then flat_pool is the preferred structure.

If your objects:

  • must reside at a stable address,
  • cannot be copied or moved,
  • or must release heavy resources exactly when the last reference disappears,

then a pointer-based pool should be used instead.

Note
The implementation of flat_pool is algorithmically data-race-free (DRF) and does not rely on undefined behavior under the ISO C++ memory model.
Additional std::atomic_thread_fence(std::memory_order_seq_cst) barriers are introduced on Windows builds to strengthen ordering at the language level. This removes ISO-level UB risks and preserves correctness within the C++ abstract machine.
However, Windows runtime and system-level synchronization primitives (e.g. SRWLock-based std::shared_mutex implementations) do not necessarily provide POSIX-equivalent global ordering behavior. Under extreme multi-core contention, rare visibility or reordering phenomena may still be observed.
Such behavior is not a violation of the C++ standard and does not indicate undefined behavior in flat_pool; rather, it reflects platform-level memory ordering characteristics outside the control of the library.
Accordingly, while API correctness is preserved, Windows builds are not guaranteed to exhibit the same high-pressure stability characteristics as POSIX systems.
Version
1.4.x
Date
2025