Data-Heap-Shared
view release on metacpan or search on metacpan
if (!heap_pid_alive(pid)) {
if (__atomic_compare_exchange_n(&hdr->mutex, &cur, 0,
0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
__atomic_add_fetch(&hdr->stat_recoveries, 1, __ATOMIC_RELAXED);
/* Wake one waiter so recovery latency is not bounded by the 2s timeout. */
if (__atomic_load_n(&hdr->mutex_waiters, __ATOMIC_RELAXED) > 0)
syscall(SYS_futex, &hdr->mutex, FUTEX_WAKE, 1, NULL, NULL, 0);
}
}
}
}
__atomic_sub_fetch(&hdr->mutex_waiters, 1, __ATOMIC_RELAXED);
spin = 0;
}
}
static inline void heap_mutex_unlock(HeapHeader *hdr) {
__atomic_store_n(&hdr->mutex, 0, __ATOMIC_RELEASE);
if (__atomic_load_n(&hdr->mutex_waiters, __ATOMIC_RELAXED) > 0)
syscall(SYS_futex, &hdr->mutex, FUTEX_WAKE, 1, NULL, NULL, 0);
}
/* ================================================================
* Heap operations (must hold mutex)
* ================================================================ */
static inline void heap_swap(HeapEntry *a, HeapEntry *b) {
HeapEntry t = *a; *a = *b; *b = t;
}
static inline void heap_sift_up(HeapEntry *data, uint32_t idx) {
while (idx > 0) {
uint32_t parent = (idx - 1) / 2;
if (data[parent].priority <= data[idx].priority) break;
heap_swap(&data[parent], &data[idx]);
idx = parent;
}
}
static inline void heap_sift_down(HeapEntry *data, uint32_t size, uint32_t idx) {
while (1) {
uint32_t smallest = idx;
uint64_t left = (uint64_t)idx * 2 + 1; /* uint64: 2*idx overflows uint32 near 2^31 */
uint64_t right = (uint64_t)idx * 2 + 2;
if (left < size && data[left].priority < data[smallest].priority)
smallest = (uint32_t)left;
if (right < size && data[right].priority < data[smallest].priority)
smallest = (uint32_t)right;
if (smallest == idx) break;
heap_swap(&data[idx], &data[smallest]);
idx = smallest;
}
}
/* ================================================================
* Public API
* ================================================================ */
static inline void heap_make_deadline(double t, struct timespec *dl) {
clock_gettime(CLOCK_MONOTONIC, dl);
if (!(t < 1e9)) t = 1e9; /* clamp Inf/NaN/huge: avoid UB (time_t) cast -> instant spurious timeout */
dl->tv_sec += (time_t)t;
dl->tv_nsec += (long)((t - (double)(time_t)t) * 1e9);
if (dl->tv_nsec >= 1000000000L) { dl->tv_sec++; dl->tv_nsec -= 1000000000L; }
}
static inline int heap_remaining(const struct timespec *dl, struct timespec *rem) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
rem->tv_sec = dl->tv_sec - now.tv_sec;
rem->tv_nsec = dl->tv_nsec - now.tv_nsec;
if (rem->tv_nsec < 0) { rem->tv_sec--; rem->tv_nsec += 1000000000L; }
return rem->tv_sec >= 0;
}
static inline int heap_push(HeapHandle *h, int64_t priority, int64_t value) {
HeapHeader *hdr = h->hdr;
heap_mutex_lock(hdr);
uint32_t cur = hdr->size;
if (cur >= h->capacity) {
heap_mutex_unlock(hdr);
return 0;
}
uint32_t idx = cur;
hdr->size = cur + 1;
h->data[idx].priority = priority;
h->data[idx].value = value;
heap_sift_up(h->data, idx);
__atomic_add_fetch(&hdr->stat_pushes, 1, __ATOMIC_RELAXED);
heap_mutex_unlock(hdr);
/* Wake pop-waiters. Full barrier so our size store (under the lock) is
* globally ordered before this waiters_pop load; pairs with the matching
* fence in heap_pop_wait so a concurrent waiter either observes our push
* (via the futex value re-check) or we observe its waiters_pop increment
* -- Dekker, no lost wakeup on weakly-ordered CPUs. */
__atomic_thread_fence(__ATOMIC_SEQ_CST);
if (__atomic_load_n(&hdr->waiters_pop, __ATOMIC_SEQ_CST) > 0)
syscall(SYS_futex, &hdr->size, FUTEX_WAKE, 1, NULL, NULL, 0);
return 1;
}
static inline int heap_pop(HeapHandle *h, int64_t *out_priority, int64_t *out_value) {
HeapHeader *hdr = h->hdr;
heap_mutex_lock(hdr);
uint32_t cur = hdr->size;
if (cur == 0) {
heap_mutex_unlock(hdr);
return 0;
}
/* Layer B: size is read from the shared segment and used just below as an
* array index (h->data[size]) and as the sift-down bound. A local peer
* with write access to the backing file can corrupt it past capacity,
* which would drive an out-of-bounds read/write. A valid heap always
* keeps size <= capacity (push enforces it), so this never fires for
* good data. Read it once into a local and use the local throughout. */
if (cur > h->capacity) {
heap_mutex_unlock(hdr);
return 0;
}
*out_priority = h->data[0].priority;
*out_value = h->data[0].value;
( run in 1.337 second using v1.01-cache-2.11-cpan-84e82930d8c )