Data-Buffer-Shared
view release on metacpan or search on metacpan
buf_generic.h view on Meta::CPAN
static inline void buf_rwlock_wrunlock(BufHandle *h) {
__atomic_store_n(&h->hdr->rwlock, 0, __ATOMIC_RELEASE);
if (__atomic_load_n(&h->hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
syscall(SYS_futex, &h->hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
}
/* ---- Seqlock ---- */
static inline uint32_t buf_seqlock_read_begin(BufHeader *hdr) {
int spin = 0;
for (;;) {
uint32_t s = __atomic_load_n(&hdr->seq, __ATOMIC_ACQUIRE);
if (__builtin_expect((s & 1) == 0, 1)) return s;
if (__builtin_expect(spin < 100000, 1)) {
buf_spin_pause();
spin++;
continue;
}
uint32_t val = __atomic_load_n(&hdr->rwlock, __ATOMIC_RELAXED);
if (val >= BUF_RWLOCK_WRITER_BIT) {
uint32_t pid = val & BUF_RWLOCK_PID_MASK;
if (!buf_pid_alive(pid)) {
buf_recover_stale_lock(hdr, val);
spin = 0;
continue;
}
}
struct timespec ts = {0, 1000000};
nanosleep(&ts, NULL);
spin = 0;
}
}
static inline int buf_seqlock_read_retry(uint32_t *seq, uint32_t start) {
/* Acquire FENCE (LoadLoad): the section's data loads must retire before we
* re-read seq; a plain acquire load is the wrong direction (ARM64 torn read). */
__atomic_thread_fence(__ATOMIC_ACQUIRE);
return __atomic_load_n(seq, __ATOMIC_RELAXED) != start;
}
static inline void buf_seqlock_write_begin(uint32_t *seq) {
__atomic_add_fetch(seq, 1, __ATOMIC_RELEASE); /* seq becomes odd */
/* StoreStore: publish the odd seq before the section's data writes, else an
* ARM64 reader could see an even seq with half-written data (Linux smp_wmb). */
__atomic_thread_fence(__ATOMIC_RELEASE);
}
static inline void buf_seqlock_write_end(uint32_t *seq) {
__atomic_add_fetch(seq, 1, __ATOMIC_RELEASE);
}
/* ---- mmap create/open ---- */
/* Race-safe create-or-attach with restrictive perms + symlink refusal.
* The backing file is created 0600 by default (see file_mode); a local peer
* cannot pre-create it as a symlink (O_NOFOLLOW) nor open a wider-permissioned
* copy. Sets *created=1 iff this call won the O_EXCL race and made the file. */
static int buf_secure_open(const char *path, mode_t file_mode, int *created, char *errbuf) {
for (int attempt = 0; attempt < 100; attempt++) {
int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, file_mode);
if (fd >= 0) { (void)fchmod(fd, file_mode); *created = 1; return fd; } /* exact mode: umask narrowed the create */
if (errno != EEXIST) {
snprintf(errbuf, BUF_ERR_BUFLEN, "create(%s): %s", path, strerror(errno));
return -1;
}
fd = open(path, O_RDWR | O_NOFOLLOW | O_CLOEXEC);
if (fd >= 0) { *created = 0; return fd; }
if (errno == ENOENT) continue; /* creator unlinked between our two opens; retry */
snprintf(errbuf, BUF_ERR_BUFLEN, "open(%s): %s", path, strerror(errno));
return -1;
}
snprintf(errbuf, BUF_ERR_BUFLEN, "open(%s): create/attach kept racing", path);
return -1;
}
static BufHandle *buf_create_map(const char *path, uint64_t capacity,
uint32_t elem_size, uint32_t variant_id,
mode_t file_mode, char *errbuf) {
errbuf[0] = '\0';
int created = 0;
int fd = buf_secure_open(path, file_mode, &created, errbuf);
if (fd < 0) {
return NULL;
}
/* Lock file for init race prevention */
if (flock(fd, LOCK_EX) < 0) {
snprintf(errbuf, BUF_ERR_BUFLEN, "flock(%s): %s", path, strerror(errno));
close(fd);
return NULL;
}
uint64_t reader_slots_off = sizeof(BufHeader); /* 128 */
uint64_t reader_slots_size = (uint64_t)BUF_READER_SLOTS * sizeof(BufReaderSlot);
uint64_t data_off = reader_slots_off + reader_slots_size; /* cache-aligned */
if (elem_size > 0 && capacity > (UINT64_MAX - data_off) / elem_size) {
snprintf(errbuf, BUF_ERR_BUFLEN, "buffer size overflow");
flock(fd, LOCK_UN);
close(fd);
return NULL;
}
uint64_t total_size = data_off + capacity * elem_size;
struct stat st;
if (fstat(fd, &st) < 0) {
snprintf(errbuf, BUF_ERR_BUFLEN, "fstat(%s): %s", path, strerror(errno));
flock(fd, LOCK_UN);
close(fd);
return NULL;
}
if (!created && st.st_size > 0 && (uint64_t)st.st_size < sizeof(BufHeader)) {
snprintf(errbuf, BUF_ERR_BUFLEN, "%s: file too small (%lld)", path, (long long)st.st_size);
flock(fd, LOCK_UN); close(fd); return NULL;
}
if (created || st.st_size == 0) {
if (ftruncate(fd, (off_t)total_size) < 0) {
snprintf(errbuf, BUF_ERR_BUFLEN, "ftruncate(%s): %s", path, strerror(errno));
flock(fd, LOCK_UN);
close(fd);
return NULL;
( run in 1.739 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )