ringbuffer: simplify the API

Use absolute indexes that we let wrap around. We can then easily detect
how much we under of overflowed by using serial number arithmetic.
Remove the Areas, we can trivially compute this ourselves, move the
logic in read/write_data.
This commit is contained in:
Wim Taymans 2017-04-20 11:25:24 +02:00
parent 90ea120d3c
commit 0b508db9fc
6 changed files with 107 additions and 138 deletions

View file

@ -182,32 +182,45 @@ loop_invoke (SpaLoop *loop,
{
PinosLoopImpl *impl = SPA_CONTAINER_OF (loop, PinosLoopImpl, loop);
bool in_thread = pthread_equal (impl->thread, pthread_self());
SpaRingbufferArea areas[2];
InvokeItem *item;
SpaResult res;
if (in_thread) {
res = func (loop, false, seq, size, data, user_data);
} else {
spa_ringbuffer_get_write_areas (&impl->buffer, areas);
if (areas[0].len < sizeof (InvokeItem)) {
pinos_log_warn ("data-loop %p: queue full", impl);
int32_t filled, avail;
uint32_t offset, l0;
filled = spa_ringbuffer_get_write_index (&impl->buffer, &offset);
if (filled < 0 || filled > impl->buffer.size) {
pinos_log_warn ("data-loop %p: queue xrun %d", impl, filled);
return SPA_RESULT_ERROR;
}
item = SPA_MEMBER (impl->buffer_data, areas[0].offset, InvokeItem);
avail = impl->buffer.size - filled;
if (avail < sizeof (InvokeItem)) {
pinos_log_warn ("data-loop %p: queue full %d", impl, avail);
return SPA_RESULT_ERROR;
}
offset &= impl->buffer.mask;
l0 = offset + avail;
if (l0 > impl->buffer.size)
l0 = impl->buffer.size - l0;
item = SPA_MEMBER (impl->buffer_data, offset, InvokeItem);
item->func = func;
item->seq = seq;
item->size = size;
item->user_data = user_data;
if (areas[0].len > sizeof (InvokeItem) + size) {
if (l0 > sizeof (InvokeItem) + size) {
item->data = SPA_MEMBER (item, sizeof (InvokeItem), void);
item->item_size = sizeof (InvokeItem) + size;
if (areas[0].len < sizeof (InvokeItem) + item->item_size)
item->item_size = areas[0].len;
if (l0 < sizeof (InvokeItem) + item->item_size)
item->item_size = l0;
} else {
item->data = SPA_MEMBER (impl->buffer_data, areas[1].offset, void);
item->item_size = areas[0].len + 1 + size;
item->data = impl->buffer_data;
item->item_size = l0 + 1 + size;
}
memcpy (item->data, data, size);
@ -231,8 +244,8 @@ event_func (SpaLoopUtils *utils,
PinosLoopImpl *impl = data;
uint32_t offset;
while (spa_ringbuffer_get_read_offset (&impl->buffer, &offset) > 0) {
InvokeItem *item = SPA_MEMBER (impl->buffer_data, offset, InvokeItem);
while (spa_ringbuffer_get_read_index (&impl->buffer, &offset) > 0) {
InvokeItem *item = SPA_MEMBER (impl->buffer_data, offset & impl->buffer.mask, InvokeItem);
item->func (impl->this.loop, true, item->seq, item->size, item->data, item->user_data);
spa_ringbuffer_read_advance (&impl->buffer, item->item_size);
}

View file

@ -38,8 +38,8 @@ typedef struct {
PinosMemblock mem;
size_t offset;
SpaRingbufferArea areas[2];
SpaEvent current;
uint32_t current_offset;
} PinosTransportImpl;
static size_t
@ -206,20 +206,21 @@ pinos_transport_add_event (PinosTransport *trans,
SpaEvent *event)
{
PinosTransportImpl *impl = (PinosTransportImpl *) trans;
SpaRingbufferArea areas[2];
size_t avail, size;
int32_t filled, avail;
uint32_t size, index;
if (impl == NULL || event == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
filled = spa_ringbuffer_get_write_index (trans->output_buffer, &index);
avail = trans->output_buffer->size - filled;
size = SPA_POD_SIZE (event);
avail = spa_ringbuffer_get_write_areas (trans->output_buffer, areas);
if (avail < size)
return SPA_RESULT_ERROR;
spa_ringbuffer_write_data (trans->output_buffer,
trans->output_data,
areas,
index & trans->output_buffer->mask,
event,
size);
spa_ringbuffer_write_advance (trans->output_buffer, size);
@ -232,18 +233,21 @@ pinos_transport_next_event (PinosTransport *trans,
SpaEvent *event)
{
PinosTransportImpl *impl = (PinosTransportImpl *) trans;
size_t avail;
int32_t avail;
uint32_t index;
if (impl == NULL || event == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
avail = spa_ringbuffer_get_read_areas (trans->input_buffer, impl->areas);
avail = spa_ringbuffer_get_read_index (trans->input_buffer, &index);
if (avail < sizeof (SpaEvent))
return SPA_RESULT_ENUM_END;
impl->current_offset = index & trans->input_buffer->mask;
spa_ringbuffer_read_data (trans->input_buffer,
trans->input_data,
impl->areas,
impl->current_offset,
&impl->current,
sizeof (SpaEvent));
@ -266,7 +270,7 @@ pinos_transport_parse_event (PinosTransport *trans,
spa_ringbuffer_read_data (trans->input_buffer,
trans->input_data,
impl->areas,
impl->current_offset,
event,
size);
spa_ringbuffer_read_advance (trans->input_buffer, size);

View file

@ -34,24 +34,18 @@ typedef struct _SpaRingbuffer SpaRingbuffer;
#include <spa/defs.h>
#include <spa/barrier.h>
typedef struct {
uint32_t offset;
uint32_t len;
} SpaRingbufferArea;
/**
* SpaRingbuffer:
* @readindex: the current read index
* @writeindex: the current write index
* @size: the size of the ringbuffer
* @size_mask: mask if @size is power of 2
* @size: the size of the ringbuffer must be power of 2
* @mask: mask as @size - 1
*/
struct _SpaRingbuffer {
volatile uint32_t readindex;
volatile uint32_t writeindex;
uint32_t size;
uint32_t mask;
uint32_t mask2;
uint32_t readindex;
uint32_t writeindex;
uint32_t size;
uint32_t mask;
};
/**
@ -74,7 +68,6 @@ spa_ringbuffer_init (SpaRingbuffer *rbuf,
rbuf->size = size;
rbuf->mask = size - 1;
rbuf->mask2 = (size << 1) - 1;
rbuf->readindex = 0;
rbuf->writeindex = 0;
@ -94,63 +87,53 @@ spa_ringbuffer_clear (SpaRingbuffer *rbuf)
rbuf->writeindex = 0;
}
static inline uint32_t
spa_ringbuffer_get_read_offset (SpaRingbuffer *rbuf,
uint32_t *offset)
/**
* spa_ringbuffer_get_read_index:
* @rbuf: a #SpaRingbuffer
* @index: the value of readindex, should be masked to get the
* offset in the ringbuffer memory
*
* Returns: number of available bytes to read. values < 0 mean
* there was an underrun. values > rbuf->size means there
* was an overrun.
*/
static inline int32_t
spa_ringbuffer_get_read_index (SpaRingbuffer *rbuf,
uint32_t *index)
{
uint32_t avail, r;
r = rbuf->readindex;
*offset = r & rbuf->mask;
avail = (rbuf->writeindex - r) & rbuf->mask2;
int32_t avail;
*index = rbuf->readindex;
avail = (int32_t) (rbuf->writeindex - *index);
spa_barrier_read();
return avail;
}
/**
* spa_ringbuffer_get_read_areas:
* spa_ringbuffer_read_data:
* @rbuf: a #SpaRingbuffer
* @areas: an array of #SpaRingbufferArea
* @buffer: memory to read from
* @offset: offset in @buffer to read from
* @data: destination memory
* @len: number of bytes to read
*
* Fill @areas with pointers to read from. The total amount of
* bytes that can be read can be obtained by summing the areas len fields.
* Read @len bytes from @rbuf starting @offset. @offset must be masked
* with the size of @rbuf and len should be smaller than the size.
*/
static inline uint32_t
spa_ringbuffer_get_read_areas (SpaRingbuffer *rbuf,
SpaRingbufferArea areas[2])
{
uint32_t avail, end, r;
avail = spa_ringbuffer_get_read_offset (rbuf, &r);
end = r + avail;
areas[0].offset = r;
areas[1].offset = 0;
if (SPA_UNLIKELY (end > rbuf->size)) {
areas[0].len = rbuf->size - r;
areas[1].len = end - rbuf->size;
} else {
areas[0].len = avail;
areas[1].len = 0;
}
return avail;
}
static inline void
spa_ringbuffer_read_data (SpaRingbuffer *rbuf,
void *buffer,
SpaRingbufferArea areas[2],
uint32_t offset,
void *data,
uint32_t size)
uint32_t len)
{
if (SPA_LIKELY (size < areas[0].len))
memcpy (data, buffer + areas[0].offset, size);
else {
memcpy (data, buffer + areas[0].offset, areas[0].len);
memcpy (data + areas[0].len, buffer, size - areas[0].len);
if (SPA_LIKELY (offset + len < rbuf->size)) {
memcpy (data, buffer + offset, len);
} else {
uint32_t l0 = rbuf->size - offset;
memcpy (data, buffer + offset, l0);
memcpy (data + l0, buffer, len - l0);
}
}
@ -166,65 +149,46 @@ spa_ringbuffer_read_advance (SpaRingbuffer *rbuf,
int32_t len)
{
spa_barrier_full();
rbuf->readindex = (rbuf->readindex + len) & rbuf->mask2;
}
static inline uint32_t
spa_ringbuffer_get_write_offset (SpaRingbuffer *rbuf,
uint32_t *offset)
{
uint32_t avail, w;
w = rbuf->writeindex;
*offset = w & rbuf->mask;
avail = rbuf->size - ((w - rbuf->readindex) & rbuf->mask2);
spa_barrier_full();
return avail;
rbuf->readindex += len;
}
/**
* spa_ringbuffer_get_write_areas:
* spa_ringbuffer_get_write_index:
* @rbuf: a #SpaRingbuffer
* @areas: an array of #SpaRingbufferArea
* @index: the value of writeindex, should be masked to get the
* offset in the ringbuffer memory
*
* Fill @areas with pointers to write to. The total amount of
* bytes that can be written can be obtained by summing the areas len fields.
* Returns: the fill level of @rbuf. values < 0 mean
* there was an underrun. values > rbuf->size means there
* was an overrun. Subsctract from the buffer size to get
* the number of bytes available for writing.
*/
static inline uint32_t
spa_ringbuffer_get_write_areas (SpaRingbuffer *rbuf,
SpaRingbufferArea areas[2])
static inline int32_t
spa_ringbuffer_get_write_index (SpaRingbuffer *rbuf,
uint32_t *index)
{
uint32_t avail, end, w;
int32_t filled;
avail = spa_ringbuffer_get_write_offset (rbuf, &w);
end = w + avail;
*index = rbuf->writeindex;
filled = (int32_t) (*index - rbuf->readindex);
spa_barrier_full();
areas[0].offset = w;
areas[1].offset = 0;
if (SPA_UNLIKELY (end > rbuf->size)) {
areas[0].len = rbuf->size - w;
areas[1].len = end - rbuf->size;
} else {
areas[0].len = avail;
areas[1].len = 0;
}
return avail;
return filled;
}
static inline void
spa_ringbuffer_write_data (SpaRingbuffer *rbuf,
void *buffer,
SpaRingbufferArea areas[2],
uint32_t offset,
void *data,
uint32_t size)
uint32_t len)
{
if (SPA_LIKELY (size < areas[0].len))
memcpy (buffer + areas[0].offset, data, size);
else {
memcpy (buffer + areas[0].offset, data, areas[0].len);
memcpy (buffer, data + areas[0].len, size - areas[0].len);
if (SPA_LIKELY (offset + len < rbuf->size)) {
memcpy (buffer + offset, data, len);
} else {
uint32_t l0 = rbuf->size - offset;
memcpy (buffer + offset, data, l0);
memcpy (buffer, data + l0, len - l0);
}
}
@ -241,7 +205,7 @@ spa_ringbuffer_write_advance (SpaRingbuffer *rbuf,
int32_t len)
{
spa_barrier_write();
rbuf->writeindex = (rbuf->writeindex + len) & rbuf->mask2;
rbuf->writeindex += len;
}

View file

@ -120,7 +120,6 @@ spa_debug_buffer (const SpaBuffer *buffer)
fprintf (stderr, " writeindex: %d\n", h->ringbuffer.writeindex);
fprintf (stderr, " size: %d\n", h->ringbuffer.size);
fprintf (stderr, " mask: %d\n", h->ringbuffer.mask);
fprintf (stderr, " mask2: %d\n", h->ringbuffer.mask2);
break;
}
case SPA_META_TYPE_SHARED:

View file

@ -391,8 +391,9 @@ pull_frames_ringbuffer (SpaALSAState *state,
snd_pcm_uframes_t offset,
snd_pcm_uframes_t frames)
{
SpaRingbufferArea areas[2];
size_t size, avail;
int32_t avail;
uint32_t index;
size_t size;
SpaALSABuffer *b;
uint8_t *src, *dst;
@ -401,17 +402,16 @@ pull_frames_ringbuffer (SpaALSAState *state,
src = b->outbuf->datas[0].data;
dst = SPA_MEMBER (my_areas[0].addr, offset * state->frame_size, uint8_t);
avail = spa_ringbuffer_get_read_areas (&b->rb->ringbuffer, areas);
avail = spa_ringbuffer_get_read_index (&b->rb->ringbuffer, &index);
size = SPA_MIN (avail, frames * state->frame_size);
spa_log_trace (state->log, "%u %u %u %u %zd %zd",
areas[0].offset, areas[0].len,
areas[1].offset, areas[1].len, offset, size);
spa_log_trace (state->log, "%u %d %zd %zd", index, avail, offset, size);
if (size > 0) {
spa_ringbuffer_read_data (&b->rb->ringbuffer,
src,
areas,
index & b->rb->ringbuffer.mask,
dst,
size);
spa_ringbuffer_read_advance (&b->rb->ringbuffer, size);

View file

@ -576,17 +576,6 @@ spa_audiomixer_node_port_send_command (SpaNode *node,
return SPA_RESULT_NOT_IMPLEMENTED;
}
static void
clear_buffer (SpaAudioMixer *this, MixerBuffer *out)
{
int16_t *op;
size_t os;
op = SPA_MEMBER (out->outbuf->datas[0].data, out->outbuf->datas[0].chunk->offset, void);
os = out->outbuf->datas[0].chunk->size;
memset (op, 0, os);
}
static void
add_port_data (SpaAudioMixer *this, MixerBuffer *out, SpaAudioMixerPort *port, int layer)
{