Add QUEUE MPMC contract.
This commit is contained in:
1 parent
d2eacaf0f6
commit
2c6f37aa5f
2 files changed
+44
-15
No files matched your search
+43
-15
@@ -29,7 +29,16 @@
|
|||||||
#include "m-mutex.h"
|
#include "m-mutex.h"
|
||||||
#include "m-atomic.h"
|
#include "m-atomic.h"
|
||||||
|
|
||||||
/* Define the different kind of policy a buffer can have. */
|
/* Define the different kind of policy a buffer can have:
|
||||||
|
* - the buffer can be either a queue (policy is FIFO) or a stack (policy is FILO),
|
||||||
|
* - if the push is blocking (waiting for the buffer to has some space) or not,
|
||||||
|
* - if the pop is blocking (waiting for the buffer to has some space) or not,
|
||||||
|
* - if both push / pop are blocking,
|
||||||
|
* - if it shall be thread safe or not (i.e. remove mutex and atomic costs),
|
||||||
|
* - if the buffer has to be init with empty elements, or if it shall init an element when it is pushed (and moved when popped),
|
||||||
|
* - if the buffer has to overwrite the last element if the buffer is full,
|
||||||
|
* - if the pop of an element is not complete until the call to pop_release (preventing push until this call).
|
||||||
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BUFFER_QUEUE = 0, BUFFER_STACK = 1,
|
BUFFER_QUEUE = 0, BUFFER_STACK = 1,
|
||||||
BUFFER_BLOCKING_PUSH = 0, BUFFER_UNBLOCKING_PUSH = 2,
|
BUFFER_BLOCKING_PUSH = 0, BUFFER_UNBLOCKING_PUSH = 2,
|
||||||
@@ -347,6 +356,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
} \
|
} \
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Definition of a a QUEUE for Many Produccer / Many Consummer
|
/* Definition of a a QUEUE for Many Produccer / Many Consummer
|
||||||
for high bandwidth scenario:
|
for high bandwidth scenario:
|
||||||
* lock-free,
|
* lock-free,
|
||||||
@@ -359,6 +369,13 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
*/
|
*/
|
||||||
#define QUEUEI_MPMC_DEF(arg) QUEUEI_MPMC_DEF2 arg
|
#define QUEUEI_MPMC_DEF(arg) QUEUEI_MPMC_DEF2 arg
|
||||||
|
|
||||||
|
#define QUEUEI_MPMC_CONTRACT(v) do { \
|
||||||
|
assert (v != 0); \
|
||||||
|
assert (v->Tab != NULL); \
|
||||||
|
/* ProdIdx >= ConsoIdx, but due to atomic constraints, we can't check it reliabely */ \
|
||||||
|
assert (M_POWEROF2_P(v->size)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define QUEUEI_MPMC_DEF2(name, type, policy, oplist, buffer_t) \
|
#define QUEUEI_MPMC_DEF2(name, type, policy, oplist, buffer_t) \
|
||||||
\
|
\
|
||||||
/* The sequence number of an element will be equal to either \
|
/* The sequence number of an element will be equal to either \
|
||||||
@@ -383,6 +400,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
static inline bool \
|
static inline bool \
|
||||||
M_C(name, _push)(buffer_t table, type x) \
|
M_C(name, _push)(buffer_t table, type x) \
|
||||||
{ \
|
{ \
|
||||||
|
QUEUEI_MPMC_CONTRACT(table); \
|
||||||
unsigned long long idx = atomic_load(&table->ProdIdx); \
|
unsigned long long idx = atomic_load(&table->ProdIdx); \
|
||||||
const unsigned int i = idx & (table->size -1); \
|
const unsigned int i = idx & (table->size -1); \
|
||||||
const unsigned long long seq = atomic_load(&table->Tab[i].seq); \
|
const unsigned long long seq = atomic_load(&table->Tab[i].seq); \
|
||||||
@@ -394,18 +412,20 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
/* Thread has been preemptted by another one. */ \
|
/* Thread has been preemptted by another one. */ \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
||||||
M_GET_SET oplist (table->Tab[i].x, x); \
|
M_GET_SET oplist (table->Tab[i].x, x); \
|
||||||
} else { \
|
} else { \
|
||||||
M_GET_INIT_SET oplist (table->Tab[i].x, x); \
|
M_GET_INIT_SET oplist (table->Tab[i].x, x); \
|
||||||
} \
|
} \
|
||||||
atomic_store(&table->Tab[i].seq, 2*idx); \
|
atomic_store(&table->Tab[i].seq, 2*idx); \
|
||||||
return true; \
|
QUEUEI_MPMC_CONTRACT(table); \
|
||||||
|
return true; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline bool \
|
static inline bool \
|
||||||
M_C(name, _pop)(type *ptr, buffer_t table) \
|
M_C(name, _pop)(type *ptr, buffer_t table) \
|
||||||
{ \
|
{ \
|
||||||
|
QUEUEI_MPMC_CONTRACT(table); \
|
||||||
unsigned long long iC = atomic_load(&table->ConsoIdx); \
|
unsigned long long iC = atomic_load(&table->ConsoIdx); \
|
||||||
const unsigned int i = (iC & (table->size -1)); \
|
const unsigned int i = (iC & (table->size -1)); \
|
||||||
const unsigned long long seq = atomic_load(&table->Tab[i].seq); \
|
const unsigned long long seq = atomic_load(&table->Tab[i].seq); \
|
||||||
@@ -417,13 +437,14 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
/* Thread has been preempted by another one */ \
|
/* Thread has been preempted by another one */ \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
||||||
M_GET_SET oplist (*ptr , table->Tab[i].x); \
|
M_GET_SET oplist (*ptr , table->Tab[i].x); \
|
||||||
} else { \
|
} else { \
|
||||||
M_DO_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \
|
M_DO_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \
|
||||||
} \
|
} \
|
||||||
atomic_store(&table->Tab[i].seq, 2*iC + 1); \
|
atomic_store(&table->Tab[i].seq, 2*iC + 1); \
|
||||||
return true; \
|
QUEUEI_MPMC_CONTRACT(table); \
|
||||||
|
return true; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline void \
|
static inline void \
|
||||||
@@ -447,11 +468,13 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
M_GET_INIT oplist (buffer->Tab[j].x); \
|
M_GET_INIT oplist (buffer->Tab[j].x); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
QUEUEI_MPMC_CONTRACT(buffer); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline void \
|
static inline void \
|
||||||
M_C(name, _clear)(buffer_t buffer) \
|
M_C(name, _clear)(buffer_t buffer) \
|
||||||
{ \
|
{ \
|
||||||
|
QUEUEI_MPMC_CONTRACT(buffer); \
|
||||||
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
||||||
for(unsigned int j = 0; j < buffer->size; j++) { \
|
for(unsigned int j = 0; j < buffer->size; j++) { \
|
||||||
M_GET_CLEAR oplist (buffer->Tab[j].x); \
|
M_GET_CLEAR oplist (buffer->Tab[j].x); \
|
||||||
@@ -469,29 +492,34 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
M_GET_FREE oplist (buffer->Tab); \
|
M_GET_FREE oplist (buffer->Tab); \
|
||||||
|
buffer->Tab = NULL; /* safer */ \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline size_t \
|
static inline size_t \
|
||||||
M_C(name, _size)(buffer_t table) \
|
M_C(name, _size)(buffer_t table) \
|
||||||
{ \
|
{ \
|
||||||
|
QUEUEI_MPMC_CONTRACT(table); \
|
||||||
const unsigned long long iC = atomic_load(&table->ConsoIdx); \
|
const unsigned long long iC = atomic_load(&table->ConsoIdx); \
|
||||||
const unsigned long long iP = atomic_load(&table->ProdIdx); \
|
const unsigned long long iP = atomic_load(&table->ProdIdx); \
|
||||||
/* We return an approximation as we can't read both iC & iP atomically*/ \
|
/* We return an approximation as we can't read both iC & iP atomically*/ \
|
||||||
/* As we read producer index after consummer index, \
|
/* As we read producer index after consummer index, \
|
||||||
and they are atomic variables without reordering \
|
and they are atomic variables without reordering \
|
||||||
producer index is always greater or equal than consummer index */ \
|
producer index is always greater or equal than consummer index */ \
|
||||||
|
assert (iP >= iC); \
|
||||||
return iP-iC; \
|
return iP-iC; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline bool \
|
static inline bool \
|
||||||
M_C(name, _empty_p)(buffer_t v) \
|
M_C(name, _empty_p)(buffer_t v) \
|
||||||
{ \
|
{ \
|
||||||
|
QUEUEI_MPMC_CONTRACT(v); \
|
||||||
return M_C(name, _size) (v) == 0; \
|
return M_C(name, _size) (v) == 0; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline bool \
|
static inline bool \
|
||||||
M_C(name, _full_p)(buffer_t v) \
|
M_C(name, _full_p)(buffer_t v) \
|
||||||
{ \
|
{ \
|
||||||
|
QUEUEI_MPMC_CONTRACT(v); \
|
||||||
return M_C(name, _size)(v) == v->size; \
|
return M_C(name, _size)(v) == v->size; \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
#undef BUFFERI_CONTRACT
|
#undef BUFFERI_CONTRACT
|
||||||
#undef ILISTI_CONTRACT
|
#undef ILISTI_CONTRACT
|
||||||
#undef DEQUEI_CONTRACT
|
#undef DEQUEI_CONTRACT
|
||||||
|
#undef QUEUEI_MPMC_CONTRACT
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
#else
|
#else
|
||||||
/* For normal builds, empty macros which does nothing */
|
/* For normal builds, empty macros which does nothing */
|
||||||
|
|||||||
Reference in new issue
Block a user