Data-Pool-Shared

 view release on metacpan or  search on metacpan

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

offset, spreading concurrent allocators across the bitmap to cut CAS contention --
but that makes the allocated ids non-reproducible across runs. For a single
allocator that wants deterministic, sequential ids, C<scan_from($slot)> pins the
scan start to C<$slot>'s 64-slot word (pass C<0> for low-to-high order). It is
B<per-handle> (does not affect other processes), takes effect on the next
C<alloc>, and an out-of-range C<$slot> wraps. The bitmap is still the source of
truth, so this only changes I<where> the scan starts, never correctness. All
typed variants inherit it.

=head2 Batch Operations

    my $slots = $pool->alloc_n($n);            # allocate N slots (blocking)
    my $slots = $pool->alloc_n($n, $timeout);  # with timeout
    my $slots = $pool->alloc_n($n, 0);         # non-blocking
    # returns arrayref of indices, or undef (all-or-nothing)

    my $freed = $pool->free_n(\@indices);      # batch free, returns count freed
    # single used-decrement + single futex wake (faster than N individual frees)

    my $slots = $pool->allocated_slots;  # arrayref of all allocated indices

=head2 Data Access

    my $val = $pool->get($idx);         # read slot
    $pool->set($idx, $val);             # write slot

For I64/I32 variants:

    my $ok  = $pool->cas($idx, $old, $new);     # atomic CAS, returns bool
    my $old = $pool->cmpxchg($idx, $old, $new); # atomic CAS, returns old value
    my $old = $pool->xchg($idx, $val);          # atomic exchange, returns old
    my $val = $pool->add($idx, $delta);          # atomic add, returns new value
    my $val = $pool->incr($idx);                 # atomic increment
    my $val = $pool->decr($idx);                 # atomic decrement

For Str variant:

    my $max = $pool->max_len;           # maximum string length

=head2 Raw Pointers

    my $ptr = $pool->ptr($idx);     # raw C pointer to slot data (UV)
    my $ptr = $pool->data_ptr;      # pointer to start of data section

C<ptr> returns the memory address of a slot's data as an unsigned
integer. Use with L<FFI::Platypus>, OpenGL C<_c> functions, or XS
code that needs a C<void*>.

C<data_ptr> returns the base of the contiguous data region. Slots
are laid out as C<data_ptr + idx * elem_size>.

B<Warning>: The returned pointer becomes dangling if the pool object
is destroyed. Do not use after the pool goes out of scope.

=head2 Zero-Copy Access

    my $sv = $pool->slot_sv($idx);  # SV backed by slot memory

Returns a read-only scalar whose PV points directly into the shared
memory slot. Reading the returned scalar reads the slot with no C<memcpy>,
which matters for large slots. It keeps the pool object alive for as long
as that scalar (or a reference to it) is live.

Note that binding it with plain assignment makes an ordinary private
I<copy> -- Perl copies the string on assignment, so

    my $snap = $pool->slot_sv($idx);   # a SNAPSHOT of the slot right now

is frozen and does not track later writes. To keep the live zero-copy view
that continues to reflect the slot's current contents (including after a
C<free()>/re-allocation), use the return value directly or hold a reference:

    my $ref = \$pool->slot_sv($idx);   # live view; $$ref reads current slot

To modify the slot, use C<set()>.

=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



( run in 3.421 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )