Data-HashMap-Shared
view release on metacpan or search on metacpan
shm_generic.h view on Meta::CPAN
/* ---- Cache line 1 (64-127): seqlock + read-path data ---- */
uint32_t seq; /* 64: seqlock counter, odd = writer active */
uint32_t rwlock_writers_waiting; /* 68: count of writers in FUTEX_WAIT
(reader write-preferring yield signal) */
uint64_t arena_cap; /* 72: immutable, read by seqlock string path */
uint64_t reader_slots_off;/* 80: offset of reader-PID slot table for dead-reader recovery */
uint8_t _reserved1[40]; /* 88-127 */
/* ---- Cache line 2 (128-191): rwlock + write-hot fields ---- */
uint32_t rwlock; /* 128: 0=unlocked, 1..0x7FFFFFFF=readers, 0x80000000|pid=writer */
uint32_t rwlock_waiters; /* 132 */
uint32_t size; /* 136 */
uint32_t tombstones; /* 140 */
uint32_t lru_head; /* 144: MRU slot index */
uint32_t lru_tail; /* 148: LRU slot index */
uint32_t flush_cursor; /* 152: partial flush_expired scan cursor */
uint32_t table_gen; /* 156: incremented on every resize */
uint64_t arena_bump; /* 160 */
uint64_t stat_evictions; /* 168: cumulative LRU eviction count */
uint64_t stat_expired; /* 176: cumulative TTL expiration count */
shm_generic.h view on Meta::CPAN
static inline void shm_rwlock_spin_pause(void) {
#if defined(__x86_64__) || defined(__i386__)
__asm__ volatile("pause" ::: "memory");
#elif defined(__aarch64__)
__asm__ volatile("yield" ::: "memory");
#else
__asm__ volatile("" ::: "memory");
#endif
}
/* Extract writer PID from rwlock value (lower 31 bits when write-locked). */
#define SHM_RWLOCK_WRITER_BIT 0x80000000U
#define SHM_RWLOCK_PID_MASK 0x7FFFFFFFU
#define SHM_RWLOCK_WR(pid) (SHM_RWLOCK_WRITER_BIT | ((uint32_t)(pid) & SHM_RWLOCK_PID_MASK))
/* Check if a PID is alive. Returns 1 if alive or unknown, 0 if definitely dead. */
/* Liveness via kill(pid,0). NOTE: cannot detect PID reuse â if a dead
* lock-holder's PID is recycled to an unrelated live process before recovery
* runs, this reports "alive" and that slot's orphaned contribution is not
* reclaimed until the recycled process exits. Robust detection would require
* a per-slot process-start-time epoch (a header-layout/SHM_VERSION change).
shm_generic.h view on Meta::CPAN
if (__atomic_compare_exchange_n(lock, &cur, 1,
1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
return;
}
if (__builtin_expect(spin < SHM_RWLOCK_SPIN_LIMIT, 1)) {
shm_rwlock_spin_pause();
continue;
}
shm_park_reader(h);
cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
/* Sleep when write-locked OR when yielding to waiting writers */
if (cur >= SHM_RWLOCK_WRITER_BIT || cur == 0) {
long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
&shm_lock_timeout, NULL, 0);
if (rc == -1 && errno == ETIMEDOUT) {
shm_unpark_reader(h);
shm_recover_after_timeout(h);
spin = 0;
continue;
}
}
t/19-lock-exception-safety.t view on Meta::CPAN
POSIX::_exit(0); # bypass END/DESTROY noise in the child
}
my $deadline = time + $timeout;
while (time < $deadline) {
my $w = waitpid($pid, WNOHANG);
return ($? == 0) if $w == $pid;
select undef, undef, undef, 0.05;
}
kill 'KILL', $pid;
waitpid($pid, 0);
return 0; # had to be killed => deadlocked
}
# class => [ good-key, good-val-a, good-val-b ] with type-correct samples.
my @variants = (
[ 'Data::HashMap::Shared::II', 1, 10, 20 ], # int key, int val (SvIV/SvIV)
[ 'Data::HashMap::Shared::SS', 'a', 'x', 'y' ], # str key, str val (SvPV/SvPV)
[ 'Data::HashMap::Shared::IS', 1, 'x', 'y' ], # int key, str val (SvIV/SvPV)
[ 'Data::HashMap::Shared::SI', 'a', 10, 20 ], # str key, int val (SvPV/SvIV)
);
xt/edge-cases.t view on Meta::CPAN
if ($pid == 0) {
my $child_map = Data::HashMap::Shared::II->new($path, 1000);
# Do a normal put to prove the child can access the map
shm_ii_put $child_map, 2, 99;
POSIX::_exit(0);
}
waitpid($pid, 0);
# Now manually corrupt the lock to simulate the child dying while holding it.
# The header is at the start of the mmap file. rwlock is at offset 128
# and encodes 0x80000000 | pid when write-locked. seq is at offset 64.
open my $fh, '+<:raw', $path or die "Cannot open $path: $!";
# Set rwlock = 0x80000000 | $pid (write-locked by dead child)
seek($fh, 128, 0);
print $fh pack('V', 0x80000000 | $pid);
# Set seq to odd (writer active)
seek($fh, 64, 0);
print $fh pack('V', 1);
close $fh;
# Re-open the map â the stale lock is now in the mmap
undef $map;
$map = Data::HashMap::Shared::II->new($path, 1000);
( run in 1.514 second using v1.01-cache-2.11-cpan-13bb782fe5a )