/** * 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 */