32RCSID(
"$Id: c0dd4069b031c83cd1322c9a17b5a47db2713964 $")
39#include <freeradius-devel/autoconf.h>
40#include <freeradius-devel/io/atomic_queue.h>
41#include <freeradius-devel/util/math.h>
46#define atomic_int64_t _Atomic(int64_t)
47#define atomic_uint32_t _Atomic(uint32_t)
48#define atomic_uint64_t _Atomic(uint64_t)
50#define cas_incr(_store, _var) atomic_compare_exchange_strong_explicit(&_store, &_var, _var + 1, memory_order_release, memory_order_relaxed)
51#define cas_decr(_store, _var) atomic_compare_exchange_strong_explicit(&_store, &_var, _var - 1, memory_order_release, memory_order_relaxed)
52#define load(_var) atomic_load_explicit(&_var, memory_order_relaxed)
53#define acquire(_var) atomic_load_explicit(&_var, memory_order_acquire)
54#define store(_store, _var) atomic_store_explicit(&_store, _var, memory_order_release)
64#define CACHE_LINE_SIZE 128
88#define STRIPES_PER_LINE (CACHE_LINE_SIZE / sizeof(fr_atomic_queue_slot_t))
245 for (i = 0; i < size; i++) {
273 if (size == 0)
return NULL;
288 if (!chunk)
return NULL;
291 talloc_set_name_const(chunk,
"fr_atomic_queue_t");
315 if (size == 0)
return NULL;
364 if (!
aq)
return NULL;
411 if (!
data)
return false;
508#ifdef __clang_analyzer__
534 if (!p_data)
return false;
544 diff = (seq - (tail + 1));
579 *p_data = slot->
data;
634 s = malloc(
sizeof(*s));
683 if (seg_size == 0)
return NULL;
686 if (!ring)
return NULL;
694 ring->seg_size = seg_size;
709 if (!*ring_p)
return;
790 if (!
n)
return false;
808#ifdef WITH_VERIFY_PTR
847 fprintf(fp,
"AQ %p size %zu, head %" PRId64
", tail %" PRId64
"\n",
850 for (i = 0; i <
aq->
size; i++) {
855 fprintf(fp,
"\t[%zu] = { %p, %" PRId64
" }",
863 fprintf(fp,
"\tstatus %d, data_size %zd, signal %d, ack %zd, ch %p",
static fr_atomic_queue_slot_t * atomic_queue_slot(fr_atomic_queue_t *aq, int64_t pos)
Map a queue position to its slot.
static size_t atomic_queue_bytes(size_t size)
Bytes needed for a queue of size slots, header included.
fr_atomic_queue_slot_t stripe[STRIPES_PER_LINE]
atomic_int64_t head
Position of the producer.
fr_atomic_queue_t * fr_atomic_queue_malloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue outside the talloc hierarchy, owned by a talloc context.
uint8_t line_shift
High bits of a slot index give its stripe within that line: .stripe[slot >> line_shift].
bool fr_atomic_queue_pop(fr_atomic_queue_t *aq, void **p_data)
Pop a pointer from the atomic queue.
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_...
bool fr_atomic_ring_push(fr_atomic_ring_t *ring, void *data)
Push a pointer into the ring; allocate a new segment on overflow.
atomic_int64_t tail
Position of the consumer.
#define cas_incr(_store, _var)
void fr_atomic_ring_free(fr_atomic_ring_t **ring_p)
Free the ring and all remaining segments.
static void atomic_ring_segment_free(fr_atomic_ring_segment_t *s)
fr_atomic_queue_line_t line[]
The line array, aligned with cache lines to ensure producer and consumers don't conflict.
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.
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].
atomic_int64_t seq
Must be seq then data to ensure seq is 64bit aligned for 32bit address spaces.
static size_t atomic_queue_num_lines(size_t size)
Number of cache lines needed to hold size slots.
size_t size
The length of the queue.
#define STRIPES_PER_LINE
How many stripes a line contains.
size_t fr_atomic_queue_size(fr_atomic_queue_t *aq)
bool fr_atomic_queue_push(fr_atomic_queue_t *aq, void *data)
Push a pointer into the atomic queue.
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.
void fr_atomic_queue_free(fr_atomic_queue_t **aq)
Free an atomic queue if it's not freed by ctx.
#define store(_store, _var)
fr_atomic_queue_t * aq
The raw allocation to release.
fr_atomic_queue_t * q
Per-segment MPMC ring (used SPSC here).
static void atomic_queue_init(fr_atomic_queue_t *aq, size_t size)
Initialise the sequence numbers and head/tail on a fresh queue buffer.
fr_atomic_queue_t * fr_atomic_queue_talloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue.
static fr_atomic_queue_t * atomic_queue_malloc_raw(size_t size)
Create fixed-size atomic queue outside any talloc hierarchy.
size_t seg_size
Capacity of each segment.
One cache line of stripes.
Owner handle for a raw queue allocation.
Structure to hold the atomic queue.
One slot in the queue, holding one queued pointer.
struct fr_atomic_queue_s fr_atomic_queue_t
static fr_atomic_queue_t ** aq
fr_control_message_status_t status
status of this message
size_t data_size
size of the data we're sending
The header for the control message.
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.
#define atomic_load_explicit(object, order)
#define atomic_thread_fence(order)
#define atomic_store_explicit(object, desired, order)
#define atomic_init(obj, value)
TALLOC_CTX * talloc_aligned_array(TALLOC_CTX *ctx, void **start, size_t alignment, size_t size)
Return a page aligned talloc memory array.