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 )