Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | /* * Copyright (c) 2024 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #ifndef KERN_MPSC_RING_H #define KERN_MPSC_RING_H /** * @header * This is an atomic multi-producer, single-consumer ringbuffer. Producers do * not need to synchronize with each other, but only a single consumer can be * active at one time. * * @discussion * The data structures are defined here to allow them to be stored intrusively * in other structures. Their fields are only documented to aid in * understanding; do not manipulate them outside of the function interfaces * declared below. */ #include <stdint.h> #include <stdbool.h> #include <sys/cdefs.h> __BEGIN_DECLS /** * The data structure for an MPSC ringbuffer. * * @field mr_buffer * The buffer that stores data, @link mr_capacity @/link in size. Field is * constant after initialization but contents will be updated by writers. * * @field mr_capacity * The size of the buffer to store data. Field is constant after * initialization. * * @field mr_writer_holds * Any pending reservations on the buffer, sized by @link mr_writer_count * @/link. Field is constant after initialization but array elements will be * updated by writers. * * @field mr_writer_count * The number of concurrent potential writers. Field is constant after * initialization. * * @field mr_head_tail * A 64-bit value that holds the head and tail of the ringbuffer. */ struct mpsc_ring { char *mr_buffer; uint32_t *mr_writer_holds; /** * A view into the head, tail, and both offsets in the ringbuffer. * * @field mrht_head * The head offset into the ringbuffer data, where writers will write new * data. * * @field mrht_tail * The tail offset into the ringbuffer data, where the reader has read up * to. * * @field mrht_head_tail * A combined view of head and tail for atomic updates. */ union mpsc_ring_head_tail { struct { uint32_t mrht_head; uint32_t mrht_tail; }; uint64_t mrht_head_tail; } mr_head_tail; uint32_t mr_capacity; uint8_t mr_writer_count; }; /** * Initialize the ringbuffer. * * @discussion * This must be called from a preemptible context, as it allocates memory. * * @param buf * The ringbuffer to initialize. * * @param capacity_pow_2 * The size of the ringbuffer as a power of 2. For example, passing 10 here * would allocate 2^10 (1KiB) bytes. * * @param writers_max * The maximum number of writers that will be active at once. */ void mpsc_ring_init( struct mpsc_ring *buf, uint8_t capacity_pow_2, uint8_t writers_max); /** * Write data to the ringbuffer. * * @discussion * No external synchronization or mutual exclusion is necessary. * * @param buf * The ringbuffer to write data to. * * @param writer_id * The identity of the writer, must be less than the maximum writer count passed * to @link mpsc_ring_init @/link. * * @param data * The memory location to data to write to the ringbuffer. * * @param size * The size of the memory location to write. * * @return * Returns how much space was available before trying to write. * Compare this to the requested write size to determine if the data was * written. */ uint32_t mpsc_ring_write( struct mpsc_ring *buf, uint8_t writer_id, const void *data, uint32_t size); /** * A cursor to read data out of a ringbuffer. * * @discussion * This structure is defined in the header to allow it to be treated as a value. * Do not manipulate its fields manually. * * @field mrc_commit_pos * The position of the cursor that will be written back to the ringbuffer when * the read finishes. * * @field mrc_pos * The position of the cursor to read data from in @link * mpsc_ring_cursor_advance @/link. * * @field mrc_limit * The maximum position that the cursor can advance. */ typedef struct { uint32_t mrc_commit_pos; uint32_t mrc_pos; uint32_t mrc_limit; } mpsc_ring_cursor_t; /** * Read data from the ringbuffer, consuming it. * * @discussion * Only one thread may call this function at a time for the same buffer. * This function must be paired with @link mpsc_ring_read_finish @/link or * @link mpsc_ring_read_cancel @/link. * * @param buf * The ringbuffer to start reading from. * * @return * Returns a cursor to consume data from the ringbuffer. */ mpsc_ring_cursor_t mpsc_ring_read_start(struct mpsc_ring *buf); /** * Advance the cursor, copying it out of the ringbuffer and updating the next * position to advance from. * * @param buf * The ringbuffer the cursor is associated with. * * @param cursor * The cursor to advance. * * @param destination * The memory to write the ringbuffer contents into. * * @param size * The amount of ringbuffer contents to read. * * @return * True iff all the requested memory can be read, false otherwise. */ bool mpsc_ring_cursor_advance( const struct mpsc_ring *buf, mpsc_ring_cursor_t *cursor, void *destination, uint32_t size); /** * Commit any advancements in the cursor, ensuring that @link * mpsc_ring_read_finish @/link will consume the memory up to the last call to * @link mpsc_ring_cursor_advance @/link. * * @param buf * The ringbuffer the cursor is associated with. * * @param cursor * The cursor to commit advancements on. */ void mpsc_ring_cursor_commit( const struct mpsc_ring *buf, mpsc_ring_cursor_t *cursor); /** * Complete a read operation on the ringbuffer after manipulating a cursor. * * @discussion * This function msut only be called after a previous call to @link * mpsc_ring_read_start @/link. This call consumes the cursor and no further * operations can be called on it. * * @param buf * The ringbuffer to end reading on. * * @param cursor * The cursor provided by @link mpsc_ring_read_start @/link. */ void mpsc_ring_read_finish( struct mpsc_ring *buf, mpsc_ring_cursor_t cursor); /** * Cancel a read operation on the ringbuffer, destroying the cursor and rolling * back any advancement into the buffer. * * @discussion * This function must only be called after a previous call to @link * mpsc_ring_read_start @/link. This call consumes the cursor and no further * operations can be called on it. * * @param buf * The ringbuffer to cancel a read from. * * @param cursor * The cursor provided by @link mpsc_ring_read_start @/link. */ void mpsc_ring_read_cancel( struct mpsc_ring *buf, mpsc_ring_cursor_t cursor); __END_DECLS #endif /* !defined(KERN_MPSC_RING_H) */ |