Data-ReqRep-Shared
view release on metacpan or search on metacpan
my $rep_fd = $srv->reply_eventfd;
$srv->reply_notify; # signal after reply
$cli->eventfd; # create (maps to reply fd)
$cli->eventfd_consume; # drain in callback
$cli->eventfd_set($fd); # set inherited fd
For cross-process use, create both eventfds before fork() so child
inherits the fds:
my $srv = Data::ReqRep::Shared->new($path, 1024, 64, 4096);
my $req_fd = $srv->eventfd;
my $rep_fd = $srv->reply_eventfd;
if (fork() == 0) {
my $cli = Data::ReqRep::Shared::Client->new($path);
$cli->req_eventfd_set($req_fd);
$cli->eventfd_set($rep_fd);
$cli->send_notify($data); # wakes server
# EV::io $rep_fd for reply ...
exit;
}
# parent = server
my $w = EV::io $req_fd, EV::READ, sub {
$srv->eventfd_consume;
while (my ($req, $id) = $srv->recv) {
$srv->reply($id, process($req));
}
$srv->reply_notify;
};
Crash Safety
* Stale mutex -- if a process dies holding the request queue mutex,
other processes detect it via PID tracking and recover within 2
seconds.
* Stale response slots -- if a client dies while holding a slot
(ACQUIRED or READY state), the slot is reclaimed automatically
during the next slot acquisition scan.
* ABA protection -- response slot IDs carry a generation counter. A
cancelled-and-reacquired slot has a different generation, so stale
"reply"/"get"/"cancel" calls are safely rejected.
Tuning
"req_cap" -- request queue capacity (power of 2). Higher for bursty
workloads (1024-4096), lower for steady-state (64-256). Memory: 24
bytes/slot + arena (Str) or 24 bytes/slot (Int).
"resp_slots" -- max concurrent in-flight requests across all clients.
One slot per outstanding async request. For synchronous req(), one per
client suffices. Memory: 64 bytes/slot (Int) or (32 + "resp_size"
rounded up to 64) bytes/slot (Str).
"resp_size" -- max response payload bytes (Str only). Fixed per slot.
Responses exceeding this croak. Pick the 99th percentile.
"arena" -- request data arena bytes (Str only, default "req_cap * 256").
Increase for large requests. Monitor "arena_used" in stats().
Benchmarks
Linux x86_64. Run "perl -Mblib bench/vs.pl 50000" to reproduce.
SINGLE-PROCESS ECHO (200K iterations)
ReqRep::Int (lock-free) 1.8M req/s
ReqRep::Str (12B, mutex) 1.2M req/s
ReqRep::Str batch (100x) 1.4M req/s
CROSS-PROCESS ECHO (50K iterations, 12B payload)
Pipe pair (1:1) 240K req/s
Unix socketpair (1:1) 222K req/s
ReqRep::Int 202K req/s *
ReqRep::Str 177K req/s *
IPC::Msg (SysV) 165K req/s
TCP loopback 115K req/s
MCE::Channel 96K req/s
Socketpair via broker 82K req/s
Forks::Queue (Shmem) 5K req/s
"*" = MPMC with per-request reply routing. Pipes and sockets are faster
for simple 1:1 echo but require dedicated fd pairs per client-worker
connection and cannot do MPMC without a broker (which halves
throughput).
SEE ALSO
Data::Buffer::Shared - typed shared array
Data::HashMap::Shared - concurrent hash table
Data::Queue::Shared - FIFO queue
Data::PubSub::Shared - publish-subscribe ring
Data::Sync::Shared - synchronization primitives
Data::Pool::Shared - fixed-size object pool
Data::Stack::Shared - LIFO stack
Data::Deque::Shared - double-ended queue
Data::Log::Shared - append-only log (WAL)
Data::Heap::Shared - priority queue
Data::Graph::Shared - directed weighted graph
Data::BitSet::Shared - shared bitset (lock-free per-bit ops)
Data::RingBuffer::Shared - fixed-size overwriting ring buffer
AUTHOR
vividsnow
LICENSE
This is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
( run in 0.801 second using v1.01-cache-2.11-cpan-71847e10f99 )