You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
3.5 KiB
114 lines
3.5 KiB
5 years ago
|
/**
|
||
|
* Redistribution of this file is permitted under the BSD two clause license.
|
||
|
*
|
||
|
* Copyright 2019, The George Washington University
|
||
|
* Author: Phani Gadepalli, phanikishoreg@gwu.edu
|
||
|
*/
|
||
|
#ifndef DEQUE_H
|
||
|
#define DEQUE_H
|
||
|
|
||
|
/*
|
||
|
* This was implemented by referring to:
|
||
|
* https://github.com/cpp-taskflow/cpp-taskflow/blob/9c28ccec910346a9937c40db7bdb542262053f9c/taskflow/executor/workstealing.hpp
|
||
|
*
|
||
|
* which is based on the following papers:
|
||
|
*
|
||
|
* The work stealing queue described in the paper, "Dynamic Circular Work-stealing Deque," SPAA, 2015.
|
||
|
* Only the queue owner can perform pop and push operations, while others can steal data from the queue.
|
||
|
*
|
||
|
* PPoPP implementation paper, "Correct and Efficient Work-Stealing for Weak Memory Models"
|
||
|
* https://www.di.ens.fr/~zappa/readings/ppopp13.pdf
|
||
|
*/
|
||
|
//TODO: dynamic resize!
|
||
|
#define DEQUE_MAX_SZ (1<<23)
|
||
|
|
||
|
#define DEQUE_PROTOTYPE(name, type) \
|
||
|
struct deque_##name { \
|
||
|
type wrk[DEQUE_MAX_SZ]; \
|
||
|
long size; \
|
||
|
\
|
||
|
volatile long top; \
|
||
|
volatile long bottom; \
|
||
|
}; \
|
||
|
\
|
||
|
static inline void \
|
||
|
deque_init_##name(struct deque_##name *q, size_t sz) \
|
||
|
{ \
|
||
|
memset(q, 0, sizeof(struct deque_##name)); \
|
||
|
\
|
||
|
if (sz) { \
|
||
|
/* only for size with pow of 2 */ \
|
||
|
/* assert((sz & (sz - 1)) == 0); */ \
|
||
|
assert(sz <= DEQUE_MAX_SZ); \
|
||
|
} else { \
|
||
|
sz = DEQUE_MAX_SZ; \
|
||
|
} \
|
||
|
\
|
||
|
q->size = sz; \
|
||
|
} \
|
||
|
\
|
||
|
/* Use mutual exclusion locks around push/pop if multi-threaded. */ \
|
||
|
static inline int \
|
||
|
deque_push_##name(struct deque_##name *q, type *w) \
|
||
|
{ \
|
||
|
long ct, cb; \
|
||
|
\
|
||
|
ct = q->top; \
|
||
|
cb = q->bottom; \
|
||
|
\
|
||
|
/* nope, fixed size only */ \
|
||
|
if (q->size - 1 < (cb - ct)) return -ENOSPC; \
|
||
|
\
|
||
|
q->wrk[cb] = *w; \
|
||
|
__sync_synchronize(); \
|
||
|
if (__sync_bool_compare_and_swap(&q->bottom, cb, cb + 1) == false) assert(0); \
|
||
|
\
|
||
|
return 0; \
|
||
|
} \
|
||
|
\
|
||
|
/* Use mutual exclusion locks around push/pop if multi-threaded. */ \
|
||
|
static inline int \
|
||
|
deque_pop_##name(struct deque_##name *q, type *w) \
|
||
|
{ \
|
||
|
long ct = 0, sz = 0; \
|
||
|
long cb = q->bottom - 1; \
|
||
|
int ret = 0; \
|
||
|
\
|
||
|
if (__sync_bool_compare_and_swap(&q->bottom, cb + 1, cb) == false) assert(0); \
|
||
|
\
|
||
|
ct = q->top; \
|
||
|
sz = cb - ct; \
|
||
|
if (sz < 0) { \
|
||
|
if (__sync_bool_compare_and_swap(&q->bottom, cb, ct) == false) assert(0); \
|
||
|
\
|
||
|
return -ENOENT; \
|
||
|
} \
|
||
|
\
|
||
|
*w = q->wrk[cb]; \
|
||
|
if (sz > 0) return 0; \
|
||
|
\
|
||
|
ret = __sync_bool_compare_and_swap(&q->top, ct, ct + 1); \
|
||
|
if (__sync_bool_compare_and_swap(&q->bottom, cb, ct + 1) == false) assert(0); \
|
||
|
if (ret == false) { *w = NULL; return -ENOENT; } \
|
||
|
\
|
||
|
return 0; \
|
||
|
} \
|
||
|
\
|
||
|
static inline int \
|
||
|
deque_steal_##name(struct deque_##name *q, type *w) \
|
||
|
{ \
|
||
|
long ct, cb; \
|
||
|
\
|
||
|
ct = q->top; \
|
||
|
cb = q->bottom; \
|
||
|
\
|
||
|
if (ct >= cb) return -ENOENT; \
|
||
|
\
|
||
|
*w = q->wrk[ct]; \
|
||
|
if (__sync_bool_compare_and_swap(&q->top, ct, ct + 1) == false) return -EAGAIN; \
|
||
|
\
|
||
|
return 0; \
|
||
|
}
|
||
|
|
||
|
#endif /* DEQUE_H */
|