File: pcbuf.h

package info (click to toggle)
fio 3.41-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,012 kB
  • sloc: ansic: 82,290; python: 9,862; sh: 6,067; makefile: 813; yacc: 204; lex: 184
file content (211 lines) | stat: -rw-r--r-- 6,017 bytes parent folder | download
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 */