一個無鎖多線程安全的ring buffer實現
跨平臺,無鎖,多線程讀寫安全,測試通過。
ringbuf.h
/***********************************************************************
* Copyright (c) 2008-2080 pepstack.com, [email protected]
*
* ALL RIGHTS RESERVED.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************/
/**
* @filename ringbuf.h
* Multi-threads Safty Ring Buffer.
*
* This code has been tested OK on 20 threads!
*
* @author Liang Zhang <[email protected]>
* @version 1.0.0
* @create 2019-12-14 12:46:50
* @update 2019-12-23 21:20:10
*/
/**************************************************************
* Length(L) = 10, Read(R), Write(W), wrap factor=0,1
* Space(S)
*
* + R W + +
* |--+--+--+--+--+--+--+--+--+--|--+--+--+--+--+--+--+--+--+--|--+--+--+--
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
*
* S = L - (fL + W - R), W - R < L
* D = fL + W - R
*
* S > 0: writable
* D > 0: readable
*************************************************************/
#ifndef _RINGBUF_H_
#define _RINGBUF_H_
#if defined(__cplusplus)
extern "C"
{
#endif
#include "uatomic.h"
#define RINGBUF_LENGTH_MAX 0x01FFFF
/* must > 1 */
#define RINGBUF_LENGTH_MIN 2
typedef struct
{
size_t size;
char data[0];
} ringbuf_elt_t;
static char * ringbuf_elt_new (size_t bufsize, ringbuf_elt_t **eltp)
{
ringbuf_elt_t *elt = (ringbuf_elt_t *) malloc(sizeof(*elt) + bufsize);
if (! elt) {
/* out memory */
*eltp = 0;
exit(EXIT_FAILURE);
}
elt->size = bufsize;
*eltp = elt;
return elt->data;
}
#define ringbuf_elt_free(elt) free(elt)
typedef struct
{
/* MT-safety Read Lock */
uatomic_int RLock;
/* MT-safety Write Lock */
uatomic_int WLock;
/* Wrap factor: 0 or l */
uatomic_int f;
/* Write (push) index: 0, L-1 */
uatomic_int W;
/* Read (pop) index: 0, L-1 */
uatomic_int R;
/* Length of ring buffer */
int L;
/* Buffer */
ringbuf_elt_t *B[0];
} ringbuf_t;
/**
* public interface
*/
static ringbuf_t * ringbuf_init(int length)
{
ringbuf_t *rb;
if (length < RINGBUF_LENGTH_MIN) {
length = RINGBUF_LENGTH_MIN;
}
if (length > RINGBUF_LENGTH_MAX) {
length = RINGBUF_LENGTH_MAX;
}
/* new and initialize read and write index by 0 */
rb = (ringbuf_t *) calloc(1, sizeof(*rb) + sizeof(ringbuf_elt_t*) * length);
if (!rb) {
/* out of memory */
exit(EXIT_FAILURE);
}
rb->L = length;
return rb;
}
static void ringbuf_uninit(ringbuf_t *rb)
{
uatomic_int_set(&rb->RLock, 1);
uatomic_int_set(&rb->WLock, 1);
int i = rb->L;
while (i-- > 0) {
ringbuf_elt_t *elt = rb->B[i];
if (elt) {
rb->B[i] = 0;
free(elt);
}
}
free(rb);
}
/**
* -----------------------------------------------
* Write Sequence | f | R | Sw = L+R-f*L-W
* ------------------+-----+-----+----------------
* (1) w1,w2,r3,r4 | 1 | r | R-W
* (2) w1,r3,w2,r4 | 1 | 0 | -W
* (3) w1,r3,r4,w2 | 1 | 0 | -W
* (4) r3,r4,w1,w2 | 0 | 0 | L-W
* (5) r3,w1,r4,w2 | 1 | 0 | -W
* (6) r3,w1,w2,r4 | 1 | 0 | -W
* -----------------------------------------------
*/
static int ringbuf_push (ringbuf_t *rb, ringbuf_elt_t *elt)
{
if (! uatomic_int_comp_exch(&rb->WLock, 0, 1)) {
/* w1: copy wrap factor */
int f = uatomic_int_get(&rb->f);
/* w2: copy read index */
int R = uatomic_int_get(&rb->R);
int L = rb->L, W = rb->W;
/* Sw = L - (f*L+W - R) */
if (L + R - f*L - W > 0) {
/* writable: Sw > 0 */
rb->B[W] = elt;
if (W == L - 1) {
/* w3: W = 0 */
uatomic_int_zero(&rb->W);
/* w4: f = 1 */
uatomic_int_set(&rb->f, 1);
} else {
/* W++ */
uatomic_int_add(&rb->W);
}
/* push success */
uatomic_int_zero(&rb->WLock);
return 1;
}
uatomic_int_zero(&rb->WLock);
}
/* push failed */
return 0;
}
/**
* -----------------------------------------------
* Read Sequence | f | W | Sr = f*L+W-R
* ------------------+-----+-----+----------------
* (1) r1,r2,w3,w4 | 0 | w | W-R
* (2) r1,w3,r2,w4 | 0 | 0 | -R
* (3) r1,w3,w4,r2 | 0 | 0 | -R
* (4) w3,w4,r1,r2 | 1 | 0 | L-R
* (5) w3,r1,w4,r2 | 0 | 0 | -R
* (6) w3,r1,r2,w4 | 0 | 0 | -R
* -----------------------------------------------
*/
static int ringbuf_pop (ringbuf_t *rb, ringbuf_elt_t **eltp)
{
if (! uatomic_int_comp_exch(&rb->RLock, 0, 1)) {
/* r1: copy wrap factor */
int f = uatomic_int_get(&rb->f);
/* r2: copy write index */
int W = uatomic_int_get(&rb->W);
int L = rb->L, R = rb->R;
/* Sr = f*L + W - R */
if (f*L + W - R > 0) {
/* readable: Sr > 0 */
*eltp = rb->B[R];
/* clear elt */
rb->B[R] = 0;
if (R == L - 1) {
/* r3: R = 0 */
uatomic_int_zero(&rb->R);
/* r4: f = 0 */
uatomic_int_zero(&rb->f);
} else {
/* R++ */
uatomic_int_add(&rb->R);
}
/* pop success */
uatomic_int_zero(&rb->RLock);
return 1;
}
uatomic_int_zero(&rb->RLock);
}
/* pop failed */
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* _RINGBUF_H_ */
uatomic.h
/***********************************************************************
* Copyright (c) 2008-2080 pepstack.com, [email protected]
*
* ALL RIGHTS RESERVED.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************/
/**
* @filename uatomic.h
* user CAS atomic api both for Windows and Linux
*
* Usage:
* uatomic_int i;
* uatomic_int_set(&i, 0);
* uatomic_int_add(&i);
*
* @author Liang Zhang <[email protected]>
* @version 0.0.1
* @create 2019-12-15 12:46:50
* @update 2019-12-15 14:12:10
*/
#ifndef _U_ATOMIC_H_
#define _U_ATOMIC_H_
#if defined(__cplusplus)
extern "C"
{
#endif
#if defined(_WIN32)
// Windows
# include <Windows.h>
# include <process.h>
# include <stdint.h>
# include <time.h>
# include <limits.h>
# ifdef _LINUX_GNUC
# undef _LINUX_GNUC
# endif
#elif defined(__GNUC__)
// Linux
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)
#error GCC version must be greater or equal than 4.1.2
#endif
# include <sched.h>
# include <pthread.h>
# include <unistd.h>
# include <stdint.h>
# include <time.h>
# include <signal.h>
# include <errno.h>
# include <limits.h>
# include <sys/time.h>
# include <sys/sysinfo.h>
# ifndef _LINUX_GNUC
# define _LINUX_GNUC
# endif
# ifdef _WIN32
# undef _WIN32
# endif
#else
// TODO: MACOSX
# error Currently only Windows and Linux os are supported.
#endif
#if defined(_LINUX_GNUC)
typedef volatile int uatomic_int;
# define uatomic_int_add(a) __sync_add_and_fetch(a, 1)
# define uatomic_int_sub(a) __sync_sub_and_fetch(a, 1)
# define uatomic_int_set(a, newval) __sync_lock_test_and_set(a, newval)
# define uatomic_int_get(a) __sync_fetch_and_add(a, 0)
# define uatomic_int_zero(a) __sync_lock_release(a)
# define uatomic_int_comp_exch(a, comp, exch) __sync_val_compare_and_swap(a, comp, exch)
typedef volatile int64_t uatomic_int64;
# define uatomic_int64_add(a) uatomic_int_add(a)
# define uatomic_int64_sub(a) uatomic_int_sub(a)
# define uatomic_int64_set(a, newval) uatomic_int_set(a, newval)
# define uatomic_int64_get(a) uatomic_int_get(a)
# define uatomic_int64_zero(a) uatomic_int_zero(a)
# define uatomic_int64_comp_exch(a, comp, exch) uatomic_int_comp_exch(a, comp, exch)
typedef volatile void * uatomic_ptr;
# define uatomic_ptr_set(a, newval) uatomic_int_set(a, newval)
# define uatomic_ptr_get(a) uatomic_int_get(a)
# define uatomic_ptr_zero(a) uatomic_int_zero(a)
# define uatomic_ptr_comp_exch(a, comp, exch) uatomic_int_comp_exch(a, comp, exch)
#elif defined(_WIN32)
typedef volatile LONG uatomic_int;
# define uatomic_int_add(a) InterlockedIncrement(a)
# define uatomic_int_sub(a) InterlockedDecrement(a)
# define uatomic_int_set(a, newval) InterlockedExchange(a, newval)
# define uatomic_int_get(a) InterlockedCompareExchange(a, 0, 0)
# define uatomic_int_zero(a) InterlockedExchange(a, 0)
# define uatomic_int_comp_exch(a, comp, exch) InterlockedCompareExchange(a, exch, comp)
typedef volatile LONG64 uatomic_int64;
# define uatomic_int64_add(a) InterlockedIncrement64(a)
# define uatomic_int64_sub(a) InterlockedDecrement64(a)
# define uatomic_int64_set(a, newval) InterlockedExchange64(a, newval)
# define uatomic_int64_get(a) InterlockedCompareExchange64(a, 0, 0)
# define uatomic_int64_zero(a) InterlockedExchange64(a, 0)
# define uatomic_int64_comp_exch(a, comp, exch) InterlockedCompareExchange64(a, exch, comp)
typedef volatile PVOID uatomic_ptr;
# define uatomic_ptr_set(a, newval) InterlockedExchangePointer(a, newval)
# define uatomic_ptr_get(a) InterlockedCompareExchangePointer(a, 0, 0)
# define uatomic_ptr_zero(a) InterlockedExchangePointer(a, 0)
# define uatomic_ptr_comp_exch(a, comp, exch) InterlockedCompareExchangePointer(a, exch, comp)
#else
# error Currently only Windows and Linux os are supported.
#endif
#ifdef __cplusplus
}
#endif
#endif /* _U_ATOMIC_H_ */