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
|
/**
* SPDX-License-Identifier: GPL-2.0 only
*
* Copyright (c) 2025 Sandisk Corporation or its affiliates.
*/
/**
* Two-phase circular buffer implementation for producer/consumer separation.
*
* This header defines the data structures and inline functions for a two-phase
* circular buffer, allowing staged writes and explicit commit of data batches.
* Useful for double-buffered systems or scenarios requiring controlled visibility
* of produced data to consumers.
*/
#ifndef PHASE_CIRCULAR_BUFFER_H
#define PHASE_CIRCULAR_BUFFER_H
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
/**
* struct pc_buf - Two-phase circular buffer.
* @commit_head: Index of the next committed element in the buffer (visible to consumer).
* @staging_head: Index of the next staged (but not yet committed) element (written by producer).
* @read_tail: Index of the next element to be read by the consumer.
* @capacity: Total capacity of the buffer (number of elements).
* @buffer: Buffer data.
*
* This structure implements a two-phase circular buffer, where data is first staged
* by advancing @staging_head, and only becomes visible to the consumer when @commit_head
* is explicitly updated. This allows for controlled commit of data batches, useful in
* double-buffered systems or producer/consumer separation.
*/
struct pc_buf {
uint64_t commit_head;
uint64_t staging_head;
uint64_t read_tail;
uint64_t capacity;
uint64_t buffer[];
};
/**
* pcb_alloc - Allocate and initialize buffer.
* @capacity: Number of elements the buffer can hold.
*
* Returns a pointer to the allocated buffer, or NULL on failure.
*/
static inline struct pc_buf *pcb_alloc(uint64_t capacity)
{
size_t size = sizeof(struct pc_buf) + sizeof(uint64_t) * capacity;
struct pc_buf *cb = (struct pc_buf *)malloc(size);
if (!cb)
return NULL;
cb->commit_head = 0;
cb->staging_head = 0;
cb->read_tail = 0;
cb->capacity = capacity;
return cb;
}
/**
* pcb_is_empty - Check if the buffer is empty.
* @cb: pointer to the pc_buf structure.
*
* Returns true if the buffer has no committed data.
*/
static inline bool pcb_is_empty(const struct pc_buf *cb)
{
return cb->read_tail == cb->commit_head;
}
/**
* pcb_is_full - Check if the buffer is full.
* @cb: pointer to the pc_buf structure.
*
* Returns true if the buffer cannot accept more staged data.
*/
static inline bool pcb_is_full(const struct pc_buf *cb)
{
return ((cb->staging_head + 1) % cb->capacity) == cb->read_tail;
}
/**
* pcb_push_staged - Push a value into the staged buffer.
* @cb: pointer to the pc_buf structure.
* @value: value to be staged.
*
* Returns true if the value was successfully staged, false if the buffer is full.
*/
static inline bool pcb_push_staged(struct pc_buf *cb, uint64_t value)
{
if (pcb_is_full(cb))
return false;
cb->buffer[cb->staging_head] = value;
cb->staging_head = (cb->staging_head + 1) % cb->capacity;
return true;
}
/**
* pcb_commit - Commit the staged data to make it visible to consumers.
* @cb: pointer to the pc_buf structure.
*
* Updates the commit head to the current staging head, making
* all staged data visible to consumers. It should be called after staging data.
*/
static inline void pcb_commit(struct pc_buf *cb)
{
cb->commit_head = cb->staging_head;
}
/**
* pcb_pop - Pop a value from the committed buffer.
* @cb: pointer to the pc_buf structure.
* @out: pointer to the variable to store the popped value.
*
* Returns true if a value was successfully popped, false if the buffer is empty.
*/
static inline bool pcb_pop(struct pc_buf *cb, uint64_t *out)
{
if (pcb_is_empty(cb))
return false;
*out = cb->buffer[cb->read_tail];
cb->read_tail = (cb->read_tail + 1) % cb->capacity;
return true;
}
/**
* pcb_print_committed - Print the contents of the committed buffer.
* @cb: pointer to the pc_buf structure.
*
* This function prints all committed data in the buffer.
*/
static inline void pcb_print_committed(const struct pc_buf *cb)
{
uint64_t i = cb->read_tail;
printf("Committed buffer: ");
while (i != cb->commit_head) {
printf("%" PRIu64 " ", cb->buffer[i]);
i = (i + 1) % cb->capacity;
}
printf("\n");
}
/**
* pcb_print_staged - Print the contents of the staged buffer.
* @cb: pointer to the pc_buf structure.
*
* This function prints all staged data that has not yet been committed.
*/
static inline void pcb_print_staged(const struct pc_buf *cb)
{
uint64_t i = cb->commit_head;
printf("Staged (not visible yet): ");
while (i != cb->staging_head) {
printf("%" PRIu64 " ", cb->buffer[i]);
i = (i + 1) % cb->capacity;
}
printf("\n");
}
/**
* pcb_committed_size - Get the size of committed data in the buffer.
* @cb: pointer to the pc_buf structure.
*
* Returns the number of elements that have been committed and are visible to consumers.
*/
static inline uint64_t pcb_committed_size(const struct pc_buf *cb)
{
if (cb->commit_head >= cb->read_tail)
return cb->commit_head - cb->read_tail;
else
return cb->capacity - cb->read_tail + cb->commit_head;
}
/**
* pcb_staged_size - Get the size of staged data in the buffer.
* @cb: pointer to the pc_buf structure.
*
* Returns the number of elements that have been staged but not yet committed.
*/
static inline uint64_t pcb_staged_size(const struct pc_buf *cb)
{
if (cb->staging_head >= cb->commit_head)
return cb->staging_head - cb->commit_head;
else
return cb->capacity - cb->commit_head + cb->staging_head;
}
/**
* pcb_space_available - Check if there is space available for staging.
* @cb: pointer to the pc_buf structure.
*
* Returns true if there is space available for staging new data, false if the buffer is full.
*/
static inline bool pcb_space_available(const struct pc_buf *cb)
{
uint64_t used = pcb_committed_size(cb) + pcb_staged_size(cb);
/* keep 1 slot reserved to distinguish full from empty */
return used < (cb->capacity - 1);
}
#endif /* PHASE_CIRCULAR_BUFFER_H */
|