diff options
author | David Robillard <d@drobilla.net> | 2022-08-10 05:32:34 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-08-17 13:51:03 -0400 |
commit | 06b49a3419f35ee8c802f1f15b52cc45b546efb3 (patch) | |
tree | 6a331324a73a83d6ff4d10087cdc740c3f1af8aa /src/zix/ring.h | |
parent | c17c67cdba3045be3c4a0f65dac0039987e0d8cf (diff) | |
download | jalv-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.h | 141 |
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); /** @} |