NAME

Data::Deque::Shared - Shared-memory double-ended queue for Linux

SYNOPSIS

use Data::Deque::Shared;

my $dq = Data::Deque::Shared::Int->new(undef, 100);
$dq->push_back(1);
$dq->push_back(2);
$dq->push_front(0);
say $dq->pop_front;   # 0
say $dq->pop_back;    # 2

# String variant (fixed max_len per entry)
my $sq = Data::Deque::Shared::Str->new(undef, 100, 64);
$sq->push_back("hello");
say $sq->pop_front;   # hello

# blocking with timeout
$dq->push_back_wait(42, 5.0);
my $v = $dq->pop_front_wait(5.0);

# file-backed / memfd
$dq = Data::Deque::Shared::Int->new('/tmp/dq.shm', 100);
$dq = Data::Deque::Shared::Int->new_memfd("my_dq", 100);
my $fd = $dq->memfd;
$dq = Data::Deque::Shared::Int->new_from_fd($fd);

DESCRIPTION

Double-ended queue (deque) in shared memory. Ring buffer with CAS-based push/pop at both ends. Futex blocking when empty or full.

Linux-only. Requires 64-bit Perl. Capacity must be <= 2^31.

Used only as a FIFO (push_back + pop_front), it's effectively a fixed-slot lock-free string queue: 1.3x-4.7x faster than Data::Queue::Shared::Str under multi-producer contention, at the cost of per-slot fixed memory (capacity × max_len).

Concurrency

Push and pop are safe under multi-producer / multi-consumer workloads. Each slot carries a 64-bit control word (state + generation) that acts as a publication gate: a pusher atomically transitions the slot through empty → writing → filled, and a popper transitions it through filled → reading → empty with the generation bumped on completion. A consumer that claims position n via the head/tail CAS therefore always observes the publication transition of the corresponding push before reading the value.

drain is safe under concurrent push/pop: it spin-waits on any slot whose pusher is mid-publish, then releases each slot through the state machine. If a pusher crashes after winning its position CAS but before publishing the value (i.e. anywhere in the cursor-CAS to publish window), drain waits ~2 seconds and then force-recovers the slot via a generation bump (counted in stats->{recoveries}). A stalled- but-live pusher whose slot was force-recovered will silently drop its late publish rather than resurrect the slot as FILLED.

Compatibility

File format bumped to v2 in this release (per-slot control array added for MPMC safety). Opening a v1 file (magic DEQ1) created by Data::Deque::Shared <= 0.02 will croak on header validation. Re-create the deque with the new version; anonymous and memfd-backed usage is unaffected.

METHODS

Push / Pop

$dq->push_back($val);          $dq->push_front($val);
$dq->push_back_wait($val, $t); $dq->push_front_wait($val, $t);
my $v = $dq->pop_front;        my $v = $dq->pop_back;
my $v = $dq->pop_front_wait($t); my $v = $dq->pop_back_wait($t);

Status

$dq->size;  $dq->capacity;  $dq->is_empty;  $dq->is_full;
$dq->clear;    # NOT concurrency-safe
my $n = $dq->drain;  # concurrency-safe, returns count drained
$dq->stats;    # {size, capacity, pushes, pops, waits, timeouts, recoveries, mmap_size}

Common

$dq->path;  $dq->memfd;  $dq->sync;  $dq->unlink;

eventfd

$dq->eventfd;  $dq->notify;  $dq->eventfd_consume;
$dq->eventfd_set($fd);  $dq->fileno;

STATS

stats() returns: size, capacity, pushes, pops, waits, timeouts, recoveries, mmap_size. recoveries counts slots that drain force-skipped because a pusher crashed (or stalled > 2s) between winning the cursor CAS and publishing the value.

SECURITY

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

BENCHMARKS

Single-process (1M ops, x86_64 Linux, Perl 5.40):

push_back + pop_front (FIFO)    6.5M/s
push_back + pop_back (LIFO)     6.3M/s
push_front + pop_front (LIFO)   6.4M/s
push_front + pop_back (FIFO)    6.5M/s

Multi-process (8 workers, 200K ops each):

cap=16     5.7M/s aggregate
cap=64     5.9M/s aggregate
cap=256    5.8M/s aggregate

SEE ALSO

Data::Stack::Shared - LIFO stack

Data::Queue::Shared - FIFO queue

Data::ReqRep::Shared - request-reply

Data::Pool::Shared - fixed-size object pool

Data::Log::Shared - append-only log (WAL)

Data::Buffer::Shared - typed shared array

Data::Sync::Shared - synchronization primitives

Data::HashMap::Shared - concurrent hash table

Data::PubSub::Shared - publish-subscribe ring

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.