32RCSID(
"$Id: 274dcdfe7595fbb61bd6e651dd7e2a1212048fe1 $")
42#include <freeradius-devel/autoconf.h>
43#include <freeradius-devel/io/atomic_queue.h>
44#include <freeradius-devel/util/debug.h>
45#include <freeradius-devel/util/math.h>
46#include <freeradius-devel/util/strerror.h>
47#include <freeradius-devel/util/syserror.h>
53#define atomic_int64_t _Atomic(int64_t)
54#define atomic_uint32_t _Atomic(uint32_t)
55#define atomic_uint64_t _Atomic(uint64_t)
57#define cas_incr(_store, _var) atomic_compare_exchange_strong_explicit(&_store, &_var, _var + 1, memory_order_release, memory_order_relaxed)
58#define cas_decr(_store, _var) atomic_compare_exchange_strong_explicit(&_store, &_var, _var - 1, memory_order_release, memory_order_relaxed)
59#define load(_var) atomic_load_explicit(&_var, memory_order_relaxed)
60#define acquire(_var) atomic_load_explicit(&_var, memory_order_acquire)
61#define store(_store, _var) atomic_store_explicit(&_store, _var, memory_order_release)
72#define CACHE_LINE_SIZE 128
201 while (stripes & (stripes - 1)) stripes &= stripes - 1;
214 if (num < stripes)
return 1;
216 return num / stripes;
239 size_t idx = (
size_t)pos & (aq->
num - 1);
282 for (i = 0; i < num; i++) {
316 if ((num == 0) || (elem_len == 0))
return NULL;
318 if (stripes == 0)
return NULL;
333 if (!chunk)
return NULL;
336 talloc_set_name_const(chunk,
"fr_atomic_queue_t");
362 if ((num == 0) || (elem_len == 0))
return NULL;
364 if (stripes == 0)
return NULL;
394 (void) munmap(owner->
aq, owner->
len);
395 (void) close(owner->
fd);
422 if (!aq)
return NULL;
455 if (ftruncate(fd, (off_t)
len) < 0) {
487 bool attach = (*fd >= 0);
489 if ((num == 0) || (elem_len == 0))
return NULL;
491 if (stripes == 0)
return NULL;
498 if (*fd < 0)
return NULL;
501 aq = mmap(NULL,
len, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
502 if (aq == MAP_FAILED) {
515 if (attach && ((aq->
num != num) || (aq->
elem_len != elem_len))) {
516 fr_strerror_printf(
"Shared queue holds %zu elements of %zu bytes, expected %zu of %zu",
518 (void) munmap(aq,
len);
524 (void) munmap(aq,
len);
679#ifdef __clang_analyzer__
714 diff = (seq - (tail + 1));
804 if (
acquire(slot->
seq) != (tail + 1))
return false;
856 s = malloc(
sizeof(*s));
905 if (seg_size == 0)
return NULL;
908 if (!ring)
return NULL;
916 ring->seg_size = seg_size;
931 if (!*ring_p)
return;
1012 if (!
n)
return false;
1030#ifdef WITH_VERIFY_PTR
1050} fr_control_message_t;
1069 fprintf(fp,
"AQ %p size %zu, head %" PRId64
", tail %" PRId64
"\n",
1072 for (i = 0; i < aq->
num; i++) {
1077 fprintf(fp,
"\t[%zu] = { %p, %" PRId64
" }",
1081 fr_control_message_t *c;
1085 fprintf(fp,
"\tstatus %d, data_size %zd, signal %d, ack %zd, ch %p",
1086 c->status, c->data_size, c->signal, c->ack, c->ch);
static fr_atomic_queue_slot_t * atomic_queue_slot(fr_atomic_queue_t *aq, int64_t pos)
Map a queue position to its slot.
atomic_int64_t head
Position of the producer.
size_t len
Length of a mapping.
uint8_t line_shift
High bits of a slot index give its stripe within that line: .stripe[slot >> line_shift].
void fr_atomic_queue_debug(FILE *fp, fr_atomic_queue_t *aq)
Dump an atomic queue.
void * chunk
The start of the talloc chunk to pass to free, or NULL if this queue was allocated raw via fr_atomic_...
size_t elem_len
Size of one element.
bool fr_atomic_ring_push(fr_atomic_ring_t *ring, void *data)
Push a pointer into the ring; allocate a new segment on overflow.
int fd
The shared memory object behind a mapping, or -1 for a raw allocation.
atomic_int64_t tail
Position of the consumer.
fr_atomic_queue_t * fr_atomic_queue_malloc(TALLOC_CTX *ctx, size_t num, size_t elem_len)
Create fixed-size atomic queue outside the talloc hierarchy, owned by a talloc context.
#define cas_incr(_store, _var)
void fr_atomic_ring_free(fr_atomic_ring_t **ring_p)
Free the ring and all remaining segments.
static size_t atomic_queue_num_lines(size_t num, size_t stripes)
Number of cache lines needed to hold num slots.
static void atomic_ring_segment_free(fr_atomic_ring_segment_t *s)
uint8_t data[]
The element, 8 byte aligned as slots are a power of two bytes from a line boundary.
static int _atomic_ring_free(fr_atomic_ring_t *ring)
talloc destructor for fr_atomic_ring_t: walk the chain and free segments
static int _atomic_queue_owner_free(fr_atomic_queue_owner_t *owner)
fr_atomic_ring_t * fr_atomic_ring_alloc(TALLOC_CTX *ctx, size_t seg_size)
Allocate an empty SPSC ring.
bool fr_atomic_queue_mpmc_pop(fr_atomic_queue_t *aq, void *data)
Copy an element out of the atomic queue, any number of consumers.
static fr_atomic_ring_segment_t * atomic_ring_segment_alloc(size_t seg_size)
Allocate a fresh segment and its embedded queue.
bool fr_atomic_ring_pop(fr_atomic_ring_t *ring, void **p_data)
Pop a pointer from the ring, advancing past drained segments.
size_t line_mask
Low bits of a slot index give its line: line[slot & line_mask].
uint8_t lines[]
The lines, each exactly one cache line, aligned with cache lines to ensure producers and consumers do...
atomic_int64_t seq
The position this slot was last written or freed at.
size_t fr_atomic_queue_size(fr_atomic_queue_t *aq)
static fr_atomic_queue_t * atomic_queue_malloc_raw(size_t num, size_t elem_len)
Create fixed-size atomic queue outside any talloc hierarchy.
fr_atomic_queue_t * fr_atomic_queue_mmap(TALLOC_CTX *ctx, int *fd, size_t num, size_t elem_len)
Create or attach to a fixed-size atomic queue in shared memory, owned by a talloc context.
size_t slot_len
The line divided by the stripes: header, element, padding.
static void atomic_ring_segment_publish(fr_atomic_ring_t *ring, fr_atomic_ring_segment_t *h, fr_atomic_ring_segment_t *n)
Link a new segment after the current head and make it the head.
bool fr_atomic_queue_mpmc_push(fr_atomic_queue_t *aq, void const *data)
Copy an element into the atomic queue, any number of producers.
void fr_atomic_queue_free(fr_atomic_queue_t **aq)
Free an atomic queue if it's not freed by ctx.
bool fr_atomic_queue_spsc_push(fr_atomic_queue_t *aq, void const *data)
Copy an element into the atomic queue, one producer only.
fr_atomic_queue_t * fr_atomic_queue_talloc(TALLOC_CTX *ctx, size_t num, size_t elem_len)
Create a fixed-size atomic queue.
#define store(_store, _var)
static void atomic_queue_init(fr_atomic_queue_t *aq, size_t num, size_t elem_len)
Initialise the sequence numbers and head/tail on a fresh queue buffer.
fr_atomic_queue_t * aq
The memory to release.
fr_atomic_queue_t * q
Per-segment queue, driven with the spsc functions.
static size_t atomic_queue_stripes(size_t elem_len)
How many slots a line holds for an element size.
bool fr_atomic_queue_spsc_pop(fr_atomic_queue_t *aq, void *data)
Copy an element out of the atomic queue, one consumer only.
static size_t atomic_queue_bytes(size_t num, size_t stripes)
Bytes needed for a queue of num slots, header included.
size_t num
Elements the queue holds.
static int atomic_queue_shm_open(size_t len)
Open an anonymous shared memory object of a given length.
size_t seg_size
Capacity of each segment.
size_t stripes
Slots per line.
Owner handle for a queue outside the talloc hierarchy.
Structure to hold the atomic queue.
struct fr_atomic_queue_s fr_atomic_queue_t
static uint64_t fr_roundup_pow2_uint64(uint64_t v)
static uint8_t fr_high_bit_pos(uint64_t num)
Find the highest order high bit in an unsigned 64 bit integer.
int memfd_create(char const *name, unsigned int flags)
Emulates memfd_create with a named shared memory object.
#define atomic_load_explicit(object, order)
#define atomic_thread_fence(order)
#define atomic_store_explicit(object, desired, order)
#define atomic_init(obj, value)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
TALLOC_CTX * talloc_aligned_array(TALLOC_CTX *ctx, void **start, size_t alignment, size_t size)
Return a page aligned talloc memory array.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
static fr_sbuff_err_t char size_t * len