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.

126 lines
9.4 KiB

/**
* 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: Implement the ability to dynamically resize! Issue #89 */
#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; \
} \
/** \
* deque_steal \
* @param deque \
* @param w pointer to location to copy stolen type to \
* @returns 0 if successful, -2 if empty, -11 if unable to perform atomic operation \
*/ \
static inline int deque_steal_##name(struct deque_##name *deque, type *w) \
{ \
long ct, cb; \
\
ct = deque->top; \
cb = deque->bottom; \
\
/* Empty */ \
if (ct >= cb) return -ENOENT; \
\
*w = deque->wrk[ct]; \
if (__sync_bool_compare_and_swap(&deque->top, ct, ct + 1) == false) return -EAGAIN; \
\
return 0; \
} \
static inline bool deque_is_empty_##name(struct deque_##name *q) \
{ \
long ct = q->top; \
long cb = q->bottom; \
return ct >= cb; \
}
#endif /* DEQUE_H */