aboutsummaryrefslogtreecommitdiffstats
path: root/src/zix/ring.h
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2022-08-10 05:32:34 -0400
committerDavid Robillard <d@drobilla.net>2022-08-17 13:51:03 -0400
commit06b49a3419f35ee8c802f1f15b52cc45b546efb3 (patch)
tree6a331324a73a83d6ff4d10087cdc740c3f1af8aa /src/zix/ring.h
parentc17c67cdba3045be3c4a0f65dac0039987e0d8cf (diff)
downloadjalv-06b49a3419f35ee8c802f1f15b52cc45b546efb3.tar.gz
jalv-06b49a3419f35ee8c802f1f15b52cc45b546efb3.tar.bz2
jalv-06b49a3419f35ee8c802f1f15b52cc45b546efb3.zip
Update zix
Diffstat (limited to 'src/zix/ring.h')
-rw-r--r--src/zix/ring.h141
1 files changed, 114 insertions, 27 deletions
diff --git a/src/zix/ring.h b/src/zix/ring.h
index 8aa9a08..872f963 100644
--- a/src/zix/ring.h
+++ b/src/zix/ring.h
@@ -1,9 +1,11 @@
-// Copyright 2011-2020 David Robillard <d@drobilla.net>
+// Copyright 2011-2022 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#ifndef ZIX_RING_H
#define ZIX_RING_H
+#include "zix/allocator.h"
+#include "zix/attributes.h"
#include "zix/common.h"
#include <stdint.h>
@@ -22,99 +24,184 @@ extern "C" {
/**
A lock-free ring buffer.
- Thread-safe with a single reader and single writer, and realtime safe
- on both ends.
+ Thread-safe (with a few noted exceptions) for a single reader and single
+ writer, and realtime-safe on both ends.
*/
typedef struct ZixRingImpl ZixRing;
/**
+ A transaction for writing data in multiple parts.
+
+ The simple zix_ring_write() can be used to write an atomic message that will
+ immediately be visible to the reader, but transactions allow data to be
+ written in several chunks before being "committed" and becoming readable.
+ This can be useful for things like prefixing messages with a header without
+ needing an allocated buffer to construct the "packet".
+*/
+typedef struct {
+ uint32_t read_head; ///< Read head at the start of the transaction
+ uint32_t write_head; ///< Write head if the transaction were committed
+} ZixRingTransaction;
+
+/**
Create a new ring.
@param size Size in bytes (note this may be rounded up).
At most `size` - 1 bytes may be stored in the ring at once.
*/
ZIX_MALLOC_API
-ZixRing*
-zix_ring_new(uint32_t size);
+ZixRing* ZIX_ALLOCATED
+zix_ring_new(ZixAllocator* ZIX_NULLABLE allocator, uint32_t size);
-/**
- Destroy a ring.
-*/
+/// Destroy a ring
ZIX_API
void
-zix_ring_free(ZixRing* ring);
+zix_ring_free(ZixRing* ZIX_NULLABLE ring);
/**
Lock the ring data into physical memory.
This function is NOT thread safe or real-time safe, but it should be called
after zix_ring_new() to lock all ring memory to avoid page faults while
- using the ring (i.e. this function MUST be called first in order for the
- ring to be truly real-time safe).
-
+ using the ring.
*/
ZIX_API
void
-zix_ring_mlock(ZixRing* ring);
+zix_ring_mlock(ZixRing* ZIX_NONNULL ring);
/**
Reset (empty) a ring.
- This function is NOT thread-safe, it may only be called when there are no
- readers or writers.
+ This function is NOT thread-safe, it may only be called when there is no
+ reader or writer.
*/
ZIX_API
void
-zix_ring_reset(ZixRing* ring);
+zix_ring_reset(ZixRing* ZIX_NONNULL ring);
/**
Return the number of bytes of space available for reading.
+
+ Reader only.
*/
-ZIX_CONST_API
+ZIX_PURE_API
uint32_t
-zix_ring_read_space(const ZixRing* ring);
+zix_ring_read_space(const ZixRing* ZIX_NONNULL ring);
/**
Return the number of bytes of space available for writing.
+
+ Writer only.
*/
-ZIX_CONST_API
+ZIX_PURE_API
uint32_t
-zix_ring_write_space(const ZixRing* ring);
+zix_ring_write_space(const ZixRing* ZIX_NONNULL ring);
/**
- Return the capacity (i.e. total write space when empty).
+ Return the capacity (the total write space when empty).
+
+ This function returns a constant for any given ring, and may (but usually
+ shouldn't) be called anywhere.
*/
-ZIX_CONST_API
+ZIX_PURE_API
uint32_t
-zix_ring_capacity(const ZixRing* ring);
+zix_ring_capacity(const ZixRing* ZIX_NONNULL ring);
/**
Read from the ring without advancing the read head.
+
+ Reader only.
*/
ZIX_API
uint32_t
-zix_ring_peek(ZixRing* ring, void* dst, uint32_t size);
+zix_ring_peek(ZixRing* ZIX_NONNULL ring, void* ZIX_NONNULL dst, uint32_t size);
/**
Read from the ring and advance the read head.
+
+ Reader only.
*/
ZIX_API
uint32_t
-zix_ring_read(ZixRing* ring, void* dst, uint32_t size);
+zix_ring_read(ZixRing* ZIX_NONNULL ring, void* ZIX_NONNULL dst, uint32_t size);
/**
Skip data in the ring (advance read head without reading).
+
+ Reader only.
*/
ZIX_API
uint32_t
-zix_ring_skip(ZixRing* ring, uint32_t size);
+zix_ring_skip(ZixRing* ZIX_NONNULL ring, uint32_t size);
/**
Write data to the ring.
+
+ Writer only.
*/
ZIX_API
uint32_t
-zix_ring_write(ZixRing* ring, const void* src, uint32_t size);
+zix_ring_write(ZixRing* ZIX_NONNULL ring,
+ const void* ZIX_NONNULL src,
+ uint32_t size);
+
+/**
+ Begin a write.
+
+ The returned transaction is initially empty. Data can be written to it by
+ calling zix_ring_amend_write() one or more times, then finishing with
+ zix_ring_commit_write().
+
+ Note that the returned "transaction" is not meant to be long-lived: a call
+ to this function should be (more or less) immediately followed by calls to
+ zix_ring_amend_write() then a call to zix_ring_commit_write().
+
+ @param ring The ring to write data to.
+ @return A new empty transaction.
+*/
+ZixRingTransaction
+zix_ring_begin_write(ZixRing* ZIX_NONNULL ring);
+
+/**
+ Amend the current write with some data.
+
+ The data is written immediately after the previously amended data, as if
+ they were written contiguously with a single write call. This data is not
+ visible to the reader until zix_ring_commit_write() is called.
+
+ If any call to this function returns an error, then the transaction is
+ invalid and must not be committed. No cleanup is necessary for an invalid
+ transaction. Any bytes written while attempting the transaction will remain
+ in the free portion of the buffer and be overwritten by subsequent writes.
+
+ @param ring The ring this transaction is writing to.
+ @param tx The active transaction, from zix_ring_begin_write().
+ @param src Pointer to the data to write.
+ @param size Length of data to write in bytes.
+ @return #ZIX_STATUS_NO_MEM or #ZIX_STATUS_SUCCESS.
+*/
+ZixStatus
+zix_ring_amend_write(ZixRing* ZIX_NONNULL ring,
+ ZixRingTransaction* ZIX_NONNULL tx,
+ const void* ZIX_NONNULL src,
+ uint32_t size);
+
+/**
+ Commit the current write.
+
+ This atomically updates the state of the ring, so that the reader will
+ observe the data written during the transaction.
+
+ This function usually shouldn't be called for any transaction which
+ zix_ring_amend_write() returned an error for.
+
+ @param ring The ring this transaction is writing to.
+ @param tx The active transaction, from zix_ring_begin_write().
+ @return #ZIX_STATUS_SUCCESS.
+*/
+ZixStatus
+zix_ring_commit_write(ZixRing* ZIX_NONNULL ring,
+ const ZixRingTransaction* ZIX_NONNULL tx);
/**
@}