Data-Pool-Shared

 view release on metacpan or  search on metacpan

lib/Data/Pool/Shared.pm  view on Meta::CPAN


=head2 Status

    my $ok  = $pool->is_allocated($idx);
    my $cap = $pool->capacity;
    my $esz = $pool->elem_size;
    my $n   = $pool->used;              # allocated count
    my $n   = $pool->available;         # free count
    my $pid = $pool->owner($idx);       # PID of allocator

=head2 Recovery

    my $n = $pool->recover_stale;       # free slots owned by dead PIDs
    $pool->reset;                       # free all slots (exclusive access only)

=head2 Guards

    my ($idx, $guard) = $pool->alloc_guard;           # auto-free on scope exit
    my ($idx, $guard) = $pool->alloc_guard($timeout);
    my ($idx, $guard) = $pool->try_alloc_guard;       # non-blocking

=head2 Convenience

    my $idx = $pool->alloc_set($val);           # alloc + set
    my $idx = $pool->alloc_set($val, $timeout); # with timeout
    my $idx = $pool->try_alloc_set($val);       # non-blocking

    $pool->each_allocated(sub { my $idx = shift; ... });

=head2 Common Methods

    my $p  = $pool->path;        # backing file (undef if anon)
    my $fd = $pool->memfd;       # memfd fd (-1 if not memfd)
    $pool->sync;                 # msync to disk
    $pool->unlink;               # remove backing file
    my $s  = $pool->stats;       # diagnostic hashref

=head3 eventfd Integration

    my $fd = $pool->eventfd;           # create eventfd
    $pool->eventfd_set($fd);           # use existing fd
    my $fd = $pool->fileno;            # current eventfd (-1 if none)
    $pool->notify;                     # signal eventfd
    my $n  = $pool->eventfd_consume;   # drain counter

=head1 STATS

C<stats()> returns a hashref with diagnostic counters. All values are
approximate under concurrency.

=over

=item C<capacity> — total slot count (immutable)

=item C<elem_size> — bytes per slot (immutable)

=item C<used> — currently allocated slot count

=item C<available> — currently free slot count (C<capacity - used>)

=item C<waiters> — processes currently blocked on C<alloc>

=item C<mmap_size> — total mmap region size in bytes

=item C<allocs> — cumulative successful allocations

=item C<frees> — cumulative frees (including stale recovery)

=item C<waits> — C<alloc> calls that entered the retry loop

=item C<timeouts> — C<alloc> calls that timed out

=item C<recoveries> — slots freed by C<recover_stale>

=back

=head1 SECURITY

The shared memory region (mmap) is writable by all processes that open
it. A malicious process with write access to the backing file or memfd
can corrupt header fields (bitmap, counters, slot data) and cause other
processes to crash, spin, or return incorrect data. Do not share backing
files with untrusted processes. Use anonymous mode or memfd with
restricted fd passing for isolation.

=head1 PERFORMANCE

=over

=item * Allocation scans a bitmap of C<ceil(capacity/64)> words.
O(capacity/64) worst case, O(1) amortized with scan_hint.

=item * Each allocation is a single CAS on one bitmap word.
Under contention, CAS retries on the same word are ~10ns each.

=item * When pool is full, C<alloc> blocks on a futex (zero CPU).
Woken by a single C<FUTEX_WAKE> syscall on C<free>.

=item * C<free_n> batches N frees into a single C<used> decrement
and a single C<FUTEX_WAKE> syscall — faster than N individual frees.

=item * C<slot_sv> provides zero-copy access to slot data, avoiding
C<memcpy> overhead for large slots.

=item * Typed variants (I64, I32) use atomic load/store/CAS/add
directly on the mmap'd memory — no locking overhead.

=back

=head1 BENCHMARKS

Measured on a single-socket x86_64 Linux system, Perl 5.40.

    Single process (1M ops):
      I64 alloc + free          3.3M/s
      I64 get/set              ~10M/s
      I64 add/incr             ~10M/s
      I64 cas                   9.8M/s
      Str set (48B)            ~10M/s
      Str get (48B)             7.5M/s
      alloc_set + free          1.9M/s



( run in 0.889 second using v1.01-cache-2.11-cpan-e1769b4cff6 )