一個無鎖多線程安全的ring buffer實現

一個無鎖多線程安全的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_ */

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章