Data-SortedSet-Shared

 view release on metacpan or  search on metacpan

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

per member in score order after the lock is released, so the callback may safely
call back into the set.

=head2 Introspection and lifecycle

    $z->count; $z->max_entries; $z->stats;     # see STATS
    $z->path; $z->memfd; $z->sync; $z->unlink;     # or Class->unlink($path)
    $z->eventfd; $z->fileno; $z->notify; $z->eventfd_consume;

C<sync> flushes the mapping to its backing store and C<unlink> removes the
backing file (also callable as C<< Class->unlink($path) >>).  C<path> returns the
backing file path (C<undef> for an anonymous or memfd-backed set) and C<memfd>
returns the descriptor of a C<new_memfd> set (-1 otherwise).  The eventfd methods
let another process wait for updates: C<eventfd> lazily creates an eventfd and
returns its descriptor (croaks on failure; calling it again returns the same fd),
C<fileno> returns the current eventfd descriptor or -1, C<notify> writes a wakeup
(returning false if no eventfd is attached), and C<eventfd_consume> reads and
resets the counter, returning it as an integer or C<undef> when nothing is
pending.

=head1 SHARING ACROSS PROCESSES

The set lives in a shared mapping, so several processes operate on the same data
with no serialization layer in between.  There are three ways to share it:

=over 4

=item *

B<A backing file> -- every process calls C<< new($path, $max) >> on the same
path.  The first to arrive creates and sizes the file (serialized by an exclusive
lock); the rest map it.

=item *

B<An anonymous mapping inherited across C<fork>> -- create with
C<< new(undef, $max) >> before forking; the parent and its children then share
the one mapping.

=item *

B<A memfd> -- create with C<< new_memfd($name, $max) >> and hand its C<memfd>
descriptor to an unrelated process (over a UNIX socket with C<SCM_RIGHTS>, or
while the creator is alive via C</proc/$pid/fd/$n>), which reopens it with
C<< new_from_fd($fd) >>.

=back

    # children populate a fork-shared set; the parent reads the result
    my $z = Data::SortedSet::Shared->new(undef, 1_000_000);
    for my $k (1 .. 4) {
        unless (fork) {                                  # child
            $z->add($k * 1_000_000 + $_, rand) for 1 .. 1000;
            exit;
        }
    }
    1 while wait != -1;                                  # reap children
    print $z->count, "\n";                               # 4000

Every operation is serialized by the rwlock, so concurrent writers do not corrupt
the tree.  A writer can wake readers blocked in other processes through the
eventfd interface: it calls C<notify> after a batch, and a reader selects on
C<fileno> then drains the count with C<eventfd_consume>.

=head1 COMPLEXITY

C<score>/C<exists>/C<peek_*> are O(1); C<add>/C<remove>/C<incr>/C<rank>/
C<at_rank>/C<pop_*> and locating a range bound are O(log n); a range or iteration
of C<k> members is O(log n + k), scanning sequentially through the linked leaves.

=head1 STATS

C<stats()> returns a hashref with keys: C<count>, C<max_entries>, C<height>
(B+tree height), C<node_capacity>, C<nodes_used>, C<index_slots>, C<index_load>
(occupied fraction of the member index), C<ops> (running count of write-path
calls, whether or not they changed the set), and C<mmap_size> (bytes).

=head1 SECURITY

The mmap region is writable by all processes that open it.  Do not share backing
files with untrusted processes.

=head1 CRASH SAFETY

The write lock is a futex-based rwlock with PID-encoded ownership; if a writer
dies while holding it, the next writer detects the dead owner and recovers.
Reader slots are reclaimed similarly.  B<Limitation>: PID reuse is not detected,
which is very unlikely in practice but cannot be ruled out.

=head1 SEE ALSO

L<Data::SortedSet::Shared::Strings> (string-keyed variant, bundled with this
distribution), L<Data::Intern::Shared>, L<Data::SpatialHash::Shared>,
L<Data::HashMap::Shared>, L<Data::Heap::Shared>, L<Data::Graph::Shared>, and the
rest of the C<Data::*::Shared> family.

=head1 AUTHOR

vividsnow

=head1 LICENSE

This is free software; you can redistribute it and/or modify it under the same
terms as Perl itself.

=cut



( run in 0.458 second using v1.01-cache-2.11-cpan-bbe5e583499 )