11fb62fb0SOlivier Houchard /*
21fb62fb0SOlivier Houchard * Copyright 2010-2015 Samy Al Bahra.
31fb62fb0SOlivier Houchard * Copyright 2011 David Joseph.
41fb62fb0SOlivier Houchard * All rights reserved.
51fb62fb0SOlivier Houchard *
61fb62fb0SOlivier Houchard * Redistribution and use in source and binary forms, with or without
71fb62fb0SOlivier Houchard * modification, are permitted provided that the following conditions
81fb62fb0SOlivier Houchard * are met:
91fb62fb0SOlivier Houchard * 1. Redistributions of source code must retain the above copyright
101fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer.
111fb62fb0SOlivier Houchard * 2. Redistributions in binary form must reproduce the above copyright
121fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer in the
131fb62fb0SOlivier Houchard * documentation and/or other materials provided with the distribution.
141fb62fb0SOlivier Houchard *
151fb62fb0SOlivier Houchard * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161fb62fb0SOlivier Houchard * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171fb62fb0SOlivier Houchard * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181fb62fb0SOlivier Houchard * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191fb62fb0SOlivier Houchard * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201fb62fb0SOlivier Houchard * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211fb62fb0SOlivier Houchard * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221fb62fb0SOlivier Houchard * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231fb62fb0SOlivier Houchard * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241fb62fb0SOlivier Houchard * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251fb62fb0SOlivier Houchard * SUCH DAMAGE.
261fb62fb0SOlivier Houchard */
271fb62fb0SOlivier Houchard
281fb62fb0SOlivier Houchard #ifndef CK_FIFO_H
291fb62fb0SOlivier Houchard #define CK_FIFO_H
301fb62fb0SOlivier Houchard
311fb62fb0SOlivier Houchard #include <ck_cc.h>
321fb62fb0SOlivier Houchard #include <ck_md.h>
331fb62fb0SOlivier Houchard #include <ck_pr.h>
341fb62fb0SOlivier Houchard #include <ck_spinlock.h>
351fb62fb0SOlivier Houchard #include <ck_stddef.h>
361fb62fb0SOlivier Houchard
371fb62fb0SOlivier Houchard #ifndef CK_F_FIFO_SPSC
381fb62fb0SOlivier Houchard #define CK_F_FIFO_SPSC
391fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry {
401fb62fb0SOlivier Houchard void *value;
411fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *next;
421fb62fb0SOlivier Houchard };
431fb62fb0SOlivier Houchard typedef struct ck_fifo_spsc_entry ck_fifo_spsc_entry_t;
441fb62fb0SOlivier Houchard
451fb62fb0SOlivier Houchard struct ck_fifo_spsc {
461fb62fb0SOlivier Houchard ck_spinlock_t m_head;
471fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *head;
481fb62fb0SOlivier Houchard char pad[CK_MD_CACHELINE - sizeof(struct ck_fifo_spsc_entry *) - sizeof(ck_spinlock_t)];
491fb62fb0SOlivier Houchard ck_spinlock_t m_tail;
501fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *tail;
511fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *head_snapshot;
521fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *garbage;
531fb62fb0SOlivier Houchard };
541fb62fb0SOlivier Houchard typedef struct ck_fifo_spsc ck_fifo_spsc_t;
551fb62fb0SOlivier Houchard
561fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_fifo_spsc_enqueue_trylock(struct ck_fifo_spsc * fifo)571fb62fb0SOlivier Houchard ck_fifo_spsc_enqueue_trylock(struct ck_fifo_spsc *fifo)
581fb62fb0SOlivier Houchard {
591fb62fb0SOlivier Houchard
601fb62fb0SOlivier Houchard return ck_spinlock_trylock(&fifo->m_tail);
611fb62fb0SOlivier Houchard }
621fb62fb0SOlivier Houchard
631fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_spsc_enqueue_lock(struct ck_fifo_spsc * fifo)641fb62fb0SOlivier Houchard ck_fifo_spsc_enqueue_lock(struct ck_fifo_spsc *fifo)
651fb62fb0SOlivier Houchard {
661fb62fb0SOlivier Houchard
671fb62fb0SOlivier Houchard ck_spinlock_lock(&fifo->m_tail);
681fb62fb0SOlivier Houchard return;
691fb62fb0SOlivier Houchard }
701fb62fb0SOlivier Houchard
711fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_spsc_enqueue_unlock(struct ck_fifo_spsc * fifo)721fb62fb0SOlivier Houchard ck_fifo_spsc_enqueue_unlock(struct ck_fifo_spsc *fifo)
731fb62fb0SOlivier Houchard {
741fb62fb0SOlivier Houchard
751fb62fb0SOlivier Houchard ck_spinlock_unlock(&fifo->m_tail);
761fb62fb0SOlivier Houchard return;
771fb62fb0SOlivier Houchard }
781fb62fb0SOlivier Houchard
791fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_fifo_spsc_dequeue_trylock(struct ck_fifo_spsc * fifo)801fb62fb0SOlivier Houchard ck_fifo_spsc_dequeue_trylock(struct ck_fifo_spsc *fifo)
811fb62fb0SOlivier Houchard {
821fb62fb0SOlivier Houchard
831fb62fb0SOlivier Houchard return ck_spinlock_trylock(&fifo->m_head);
841fb62fb0SOlivier Houchard }
851fb62fb0SOlivier Houchard
861fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_spsc_dequeue_lock(struct ck_fifo_spsc * fifo)871fb62fb0SOlivier Houchard ck_fifo_spsc_dequeue_lock(struct ck_fifo_spsc *fifo)
881fb62fb0SOlivier Houchard {
891fb62fb0SOlivier Houchard
901fb62fb0SOlivier Houchard ck_spinlock_lock(&fifo->m_head);
911fb62fb0SOlivier Houchard return;
921fb62fb0SOlivier Houchard }
931fb62fb0SOlivier Houchard
941fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_spsc_dequeue_unlock(struct ck_fifo_spsc * fifo)951fb62fb0SOlivier Houchard ck_fifo_spsc_dequeue_unlock(struct ck_fifo_spsc *fifo)
961fb62fb0SOlivier Houchard {
971fb62fb0SOlivier Houchard
981fb62fb0SOlivier Houchard ck_spinlock_unlock(&fifo->m_head);
991fb62fb0SOlivier Houchard return;
1001fb62fb0SOlivier Houchard }
1011fb62fb0SOlivier Houchard
1021fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_spsc_init(struct ck_fifo_spsc * fifo,struct ck_fifo_spsc_entry * stub)1031fb62fb0SOlivier Houchard ck_fifo_spsc_init(struct ck_fifo_spsc *fifo, struct ck_fifo_spsc_entry *stub)
1041fb62fb0SOlivier Houchard {
1051fb62fb0SOlivier Houchard
1061fb62fb0SOlivier Houchard ck_spinlock_init(&fifo->m_head);
1071fb62fb0SOlivier Houchard ck_spinlock_init(&fifo->m_tail);
1081fb62fb0SOlivier Houchard
1091fb62fb0SOlivier Houchard stub->next = NULL;
1101fb62fb0SOlivier Houchard fifo->head = fifo->tail = fifo->head_snapshot = fifo->garbage = stub;
1111fb62fb0SOlivier Houchard return;
1121fb62fb0SOlivier Houchard }
1131fb62fb0SOlivier Houchard
1141fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_spsc_deinit(struct ck_fifo_spsc * fifo,struct ck_fifo_spsc_entry ** garbage)1151fb62fb0SOlivier Houchard ck_fifo_spsc_deinit(struct ck_fifo_spsc *fifo, struct ck_fifo_spsc_entry **garbage)
1161fb62fb0SOlivier Houchard {
1171fb62fb0SOlivier Houchard
118*74e9b5f2SOlivier Houchard *garbage = fifo->garbage;
1191fb62fb0SOlivier Houchard fifo->head = fifo->tail = NULL;
1201fb62fb0SOlivier Houchard return;
1211fb62fb0SOlivier Houchard }
1221fb62fb0SOlivier Houchard
1231fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_spsc_enqueue(struct ck_fifo_spsc * fifo,struct ck_fifo_spsc_entry * entry,void * value)1241fb62fb0SOlivier Houchard ck_fifo_spsc_enqueue(struct ck_fifo_spsc *fifo,
1251fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *entry,
1261fb62fb0SOlivier Houchard void *value)
1271fb62fb0SOlivier Houchard {
1281fb62fb0SOlivier Houchard
1291fb62fb0SOlivier Houchard entry->value = value;
1301fb62fb0SOlivier Houchard entry->next = NULL;
1311fb62fb0SOlivier Houchard
1321fb62fb0SOlivier Houchard /* If stub->next is visible, guarantee that entry is consistent. */
1331fb62fb0SOlivier Houchard ck_pr_fence_store();
1341fb62fb0SOlivier Houchard ck_pr_store_ptr(&fifo->tail->next, entry);
1351fb62fb0SOlivier Houchard fifo->tail = entry;
1361fb62fb0SOlivier Houchard return;
1371fb62fb0SOlivier Houchard }
1381fb62fb0SOlivier Houchard
1391fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_fifo_spsc_dequeue(struct ck_fifo_spsc * fifo,void * value)1401fb62fb0SOlivier Houchard ck_fifo_spsc_dequeue(struct ck_fifo_spsc *fifo, void *value)
1411fb62fb0SOlivier Houchard {
1421fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *entry;
1431fb62fb0SOlivier Houchard
1441fb62fb0SOlivier Houchard /*
1451fb62fb0SOlivier Houchard * The head pointer is guaranteed to always point to a stub entry.
1461fb62fb0SOlivier Houchard * If the stub entry does not point to an entry, then the queue is
1471fb62fb0SOlivier Houchard * empty.
1481fb62fb0SOlivier Houchard */
1491fb62fb0SOlivier Houchard entry = ck_pr_load_ptr(&fifo->head->next);
1501fb62fb0SOlivier Houchard if (entry == NULL)
1511fb62fb0SOlivier Houchard return false;
1521fb62fb0SOlivier Houchard
1531fb62fb0SOlivier Houchard /* If entry is visible, guarantee store to value is visible. */
1541fb62fb0SOlivier Houchard ck_pr_store_ptr_unsafe(value, entry->value);
1551fb62fb0SOlivier Houchard ck_pr_fence_store();
1561fb62fb0SOlivier Houchard ck_pr_store_ptr(&fifo->head, entry);
1571fb62fb0SOlivier Houchard return true;
1581fb62fb0SOlivier Houchard }
1591fb62fb0SOlivier Houchard
1601fb62fb0SOlivier Houchard /*
1611fb62fb0SOlivier Houchard * Recycle a node. This technique for recycling nodes is based on
1621fb62fb0SOlivier Houchard * Dmitriy Vyukov's work.
1631fb62fb0SOlivier Houchard */
1641fb62fb0SOlivier Houchard CK_CC_INLINE static struct ck_fifo_spsc_entry *
ck_fifo_spsc_recycle(struct ck_fifo_spsc * fifo)1651fb62fb0SOlivier Houchard ck_fifo_spsc_recycle(struct ck_fifo_spsc *fifo)
1661fb62fb0SOlivier Houchard {
1671fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *garbage;
1681fb62fb0SOlivier Houchard
1691fb62fb0SOlivier Houchard if (fifo->head_snapshot == fifo->garbage) {
1701fb62fb0SOlivier Houchard fifo->head_snapshot = ck_pr_load_ptr(&fifo->head);
1711fb62fb0SOlivier Houchard if (fifo->head_snapshot == fifo->garbage)
1721fb62fb0SOlivier Houchard return NULL;
1731fb62fb0SOlivier Houchard }
1741fb62fb0SOlivier Houchard
1751fb62fb0SOlivier Houchard garbage = fifo->garbage;
1761fb62fb0SOlivier Houchard fifo->garbage = garbage->next;
1771fb62fb0SOlivier Houchard return garbage;
1781fb62fb0SOlivier Houchard }
1791fb62fb0SOlivier Houchard
1801fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_fifo_spsc_isempty(struct ck_fifo_spsc * fifo)1811fb62fb0SOlivier Houchard ck_fifo_spsc_isempty(struct ck_fifo_spsc *fifo)
1821fb62fb0SOlivier Houchard {
1831fb62fb0SOlivier Houchard struct ck_fifo_spsc_entry *head = ck_pr_load_ptr(&fifo->head);
1841fb62fb0SOlivier Houchard return ck_pr_load_ptr(&head->next) == NULL;
1851fb62fb0SOlivier Houchard }
1861fb62fb0SOlivier Houchard
1871fb62fb0SOlivier Houchard #define CK_FIFO_SPSC_ISEMPTY(f) ((f)->head->next == NULL)
1881fb62fb0SOlivier Houchard #define CK_FIFO_SPSC_FIRST(f) ((f)->head->next)
1891fb62fb0SOlivier Houchard #define CK_FIFO_SPSC_NEXT(m) ((m)->next)
1901fb62fb0SOlivier Houchard #define CK_FIFO_SPSC_SPARE(f) ((f)->head)
1911fb62fb0SOlivier Houchard #define CK_FIFO_SPSC_FOREACH(fifo, entry) \
1921fb62fb0SOlivier Houchard for ((entry) = CK_FIFO_SPSC_FIRST(fifo); \
1931fb62fb0SOlivier Houchard (entry) != NULL; \
1941fb62fb0SOlivier Houchard (entry) = CK_FIFO_SPSC_NEXT(entry))
1951fb62fb0SOlivier Houchard #define CK_FIFO_SPSC_FOREACH_SAFE(fifo, entry, T) \
1961fb62fb0SOlivier Houchard for ((entry) = CK_FIFO_SPSC_FIRST(fifo); \
1971fb62fb0SOlivier Houchard (entry) != NULL && ((T) = (entry)->next, 1); \
1981fb62fb0SOlivier Houchard (entry) = (T))
1991fb62fb0SOlivier Houchard
2001fb62fb0SOlivier Houchard #endif /* CK_F_FIFO_SPSC */
2011fb62fb0SOlivier Houchard
2021fb62fb0SOlivier Houchard #ifdef CK_F_PR_CAS_PTR_2
2031fb62fb0SOlivier Houchard #ifndef CK_F_FIFO_MPMC
2041fb62fb0SOlivier Houchard #define CK_F_FIFO_MPMC
2051fb62fb0SOlivier Houchard struct ck_fifo_mpmc_entry;
2061fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer {
2071fb62fb0SOlivier Houchard struct ck_fifo_mpmc_entry *pointer;
2081fb62fb0SOlivier Houchard char *generation CK_CC_PACKED;
2091fb62fb0SOlivier Houchard } CK_CC_ALIGN(16);
2101fb62fb0SOlivier Houchard
2111fb62fb0SOlivier Houchard struct ck_fifo_mpmc_entry {
2121fb62fb0SOlivier Houchard void *value;
2131fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer next;
2141fb62fb0SOlivier Houchard };
2151fb62fb0SOlivier Houchard typedef struct ck_fifo_mpmc_entry ck_fifo_mpmc_entry_t;
2161fb62fb0SOlivier Houchard
2171fb62fb0SOlivier Houchard struct ck_fifo_mpmc {
2181fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer head;
2191fb62fb0SOlivier Houchard char pad[CK_MD_CACHELINE - sizeof(struct ck_fifo_mpmc_pointer)];
2201fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer tail;
2211fb62fb0SOlivier Houchard };
2221fb62fb0SOlivier Houchard typedef struct ck_fifo_mpmc ck_fifo_mpmc_t;
2231fb62fb0SOlivier Houchard
2241fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_mpmc_init(struct ck_fifo_mpmc * fifo,struct ck_fifo_mpmc_entry * stub)2251fb62fb0SOlivier Houchard ck_fifo_mpmc_init(struct ck_fifo_mpmc *fifo, struct ck_fifo_mpmc_entry *stub)
2261fb62fb0SOlivier Houchard {
2271fb62fb0SOlivier Houchard
2281fb62fb0SOlivier Houchard stub->next.pointer = NULL;
2291fb62fb0SOlivier Houchard stub->next.generation = NULL;
2301fb62fb0SOlivier Houchard fifo->head.pointer = fifo->tail.pointer = stub;
2311fb62fb0SOlivier Houchard fifo->head.generation = fifo->tail.generation = NULL;
2321fb62fb0SOlivier Houchard return;
2331fb62fb0SOlivier Houchard }
2341fb62fb0SOlivier Houchard
2351fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_mpmc_deinit(struct ck_fifo_mpmc * fifo,struct ck_fifo_mpmc_entry ** garbage)2361fb62fb0SOlivier Houchard ck_fifo_mpmc_deinit(struct ck_fifo_mpmc *fifo, struct ck_fifo_mpmc_entry **garbage)
2371fb62fb0SOlivier Houchard {
2381fb62fb0SOlivier Houchard
2391fb62fb0SOlivier Houchard *garbage = fifo->head.pointer;
2401fb62fb0SOlivier Houchard fifo->head.pointer = fifo->tail.pointer = NULL;
2411fb62fb0SOlivier Houchard return;
2421fb62fb0SOlivier Houchard }
2431fb62fb0SOlivier Houchard
2441fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_fifo_mpmc_enqueue(struct ck_fifo_mpmc * fifo,struct ck_fifo_mpmc_entry * entry,void * value)2451fb62fb0SOlivier Houchard ck_fifo_mpmc_enqueue(struct ck_fifo_mpmc *fifo,
2461fb62fb0SOlivier Houchard struct ck_fifo_mpmc_entry *entry,
2471fb62fb0SOlivier Houchard void *value)
2481fb62fb0SOlivier Houchard {
2491fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer tail, next, update;
2501fb62fb0SOlivier Houchard
2511fb62fb0SOlivier Houchard /*
2521fb62fb0SOlivier Houchard * Prepare the upcoming node and make sure to commit the updates
2531fb62fb0SOlivier Houchard * before publishing.
2541fb62fb0SOlivier Houchard */
2551fb62fb0SOlivier Houchard entry->value = value;
2561fb62fb0SOlivier Houchard entry->next.pointer = NULL;
2571fb62fb0SOlivier Houchard entry->next.generation = 0;
2581fb62fb0SOlivier Houchard ck_pr_fence_store_atomic();
2591fb62fb0SOlivier Houchard
2601fb62fb0SOlivier Houchard for (;;) {
2611fb62fb0SOlivier Houchard tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
2621fb62fb0SOlivier Houchard ck_pr_fence_load();
2631fb62fb0SOlivier Houchard tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
2641fb62fb0SOlivier Houchard next.generation = ck_pr_load_ptr(&tail.pointer->next.generation);
2651fb62fb0SOlivier Houchard ck_pr_fence_load();
2661fb62fb0SOlivier Houchard next.pointer = ck_pr_load_ptr(&tail.pointer->next.pointer);
2671fb62fb0SOlivier Houchard
2681fb62fb0SOlivier Houchard if (ck_pr_load_ptr(&fifo->tail.generation) != tail.generation)
2691fb62fb0SOlivier Houchard continue;
2701fb62fb0SOlivier Houchard
2711fb62fb0SOlivier Houchard if (next.pointer != NULL) {
2721fb62fb0SOlivier Houchard /*
2731fb62fb0SOlivier Houchard * If the tail pointer has an entry following it then
2741fb62fb0SOlivier Houchard * it needs to be forwarded to the next entry. This
2751fb62fb0SOlivier Houchard * helps us guarantee we are always operating on the
2761fb62fb0SOlivier Houchard * last entry.
2771fb62fb0SOlivier Houchard */
2781fb62fb0SOlivier Houchard update.pointer = next.pointer;
2791fb62fb0SOlivier Houchard update.generation = tail.generation + 1;
2801fb62fb0SOlivier Houchard ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
2811fb62fb0SOlivier Houchard } else {
2821fb62fb0SOlivier Houchard /*
2831fb62fb0SOlivier Houchard * Attempt to commit new entry to the end of the
2841fb62fb0SOlivier Houchard * current tail.
2851fb62fb0SOlivier Houchard */
2861fb62fb0SOlivier Houchard update.pointer = entry;
2871fb62fb0SOlivier Houchard update.generation = next.generation + 1;
2881fb62fb0SOlivier Houchard if (ck_pr_cas_ptr_2(&tail.pointer->next, &next, &update) == true)
2891fb62fb0SOlivier Houchard break;
2901fb62fb0SOlivier Houchard }
2911fb62fb0SOlivier Houchard }
2921fb62fb0SOlivier Houchard
2931fb62fb0SOlivier Houchard ck_pr_fence_atomic();
2941fb62fb0SOlivier Houchard
2951fb62fb0SOlivier Houchard /* After a successful insert, forward the tail to the new entry. */
2961fb62fb0SOlivier Houchard update.generation = tail.generation + 1;
2971fb62fb0SOlivier Houchard ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
2981fb62fb0SOlivier Houchard return;
2991fb62fb0SOlivier Houchard }
3001fb62fb0SOlivier Houchard
3011fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_fifo_mpmc_tryenqueue(struct ck_fifo_mpmc * fifo,struct ck_fifo_mpmc_entry * entry,void * value)3021fb62fb0SOlivier Houchard ck_fifo_mpmc_tryenqueue(struct ck_fifo_mpmc *fifo,
3031fb62fb0SOlivier Houchard struct ck_fifo_mpmc_entry *entry,
3041fb62fb0SOlivier Houchard void *value)
3051fb62fb0SOlivier Houchard {
3061fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer tail, next, update;
3071fb62fb0SOlivier Houchard
3081fb62fb0SOlivier Houchard entry->value = value;
3091fb62fb0SOlivier Houchard entry->next.pointer = NULL;
3101fb62fb0SOlivier Houchard entry->next.generation = 0;
3111fb62fb0SOlivier Houchard
3121fb62fb0SOlivier Houchard ck_pr_fence_store_atomic();
3131fb62fb0SOlivier Houchard
3141fb62fb0SOlivier Houchard tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
3151fb62fb0SOlivier Houchard ck_pr_fence_load();
3161fb62fb0SOlivier Houchard tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
3171fb62fb0SOlivier Houchard next.generation = ck_pr_load_ptr(&tail.pointer->next.generation);
3181fb62fb0SOlivier Houchard ck_pr_fence_load();
3191fb62fb0SOlivier Houchard next.pointer = ck_pr_load_ptr(&tail.pointer->next.pointer);
3201fb62fb0SOlivier Houchard
3211fb62fb0SOlivier Houchard if (ck_pr_load_ptr(&fifo->tail.generation) != tail.generation)
3221fb62fb0SOlivier Houchard return false;
3231fb62fb0SOlivier Houchard
3241fb62fb0SOlivier Houchard if (next.pointer != NULL) {
3251fb62fb0SOlivier Houchard /*
3261fb62fb0SOlivier Houchard * If the tail pointer has an entry following it then
3271fb62fb0SOlivier Houchard * it needs to be forwarded to the next entry. This
3281fb62fb0SOlivier Houchard * helps us guarantee we are always operating on the
3291fb62fb0SOlivier Houchard * last entry.
3301fb62fb0SOlivier Houchard */
3311fb62fb0SOlivier Houchard update.pointer = next.pointer;
3321fb62fb0SOlivier Houchard update.generation = tail.generation + 1;
3331fb62fb0SOlivier Houchard ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
3341fb62fb0SOlivier Houchard return false;
3351fb62fb0SOlivier Houchard } else {
3361fb62fb0SOlivier Houchard /*
3371fb62fb0SOlivier Houchard * Attempt to commit new entry to the end of the
3381fb62fb0SOlivier Houchard * current tail.
3391fb62fb0SOlivier Houchard */
3401fb62fb0SOlivier Houchard update.pointer = entry;
3411fb62fb0SOlivier Houchard update.generation = next.generation + 1;
3421fb62fb0SOlivier Houchard if (ck_pr_cas_ptr_2(&tail.pointer->next, &next, &update) == false)
3431fb62fb0SOlivier Houchard return false;
3441fb62fb0SOlivier Houchard }
3451fb62fb0SOlivier Houchard
3461fb62fb0SOlivier Houchard ck_pr_fence_atomic();
3471fb62fb0SOlivier Houchard
3481fb62fb0SOlivier Houchard /* After a successful insert, forward the tail to the new entry. */
3491fb62fb0SOlivier Houchard update.generation = tail.generation + 1;
3501fb62fb0SOlivier Houchard ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
3511fb62fb0SOlivier Houchard return true;
3521fb62fb0SOlivier Houchard }
3531fb62fb0SOlivier Houchard
3541fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_fifo_mpmc_dequeue(struct ck_fifo_mpmc * fifo,void * value,struct ck_fifo_mpmc_entry ** garbage)3551fb62fb0SOlivier Houchard ck_fifo_mpmc_dequeue(struct ck_fifo_mpmc *fifo,
3561fb62fb0SOlivier Houchard void *value,
3571fb62fb0SOlivier Houchard struct ck_fifo_mpmc_entry **garbage)
3581fb62fb0SOlivier Houchard {
3591fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer head, tail, next, update;
3601fb62fb0SOlivier Houchard
3611fb62fb0SOlivier Houchard for (;;) {
3621fb62fb0SOlivier Houchard head.generation = ck_pr_load_ptr(&fifo->head.generation);
3631fb62fb0SOlivier Houchard ck_pr_fence_load();
3641fb62fb0SOlivier Houchard head.pointer = ck_pr_load_ptr(&fifo->head.pointer);
3651fb62fb0SOlivier Houchard tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
3661fb62fb0SOlivier Houchard ck_pr_fence_load();
3671fb62fb0SOlivier Houchard tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
3681fb62fb0SOlivier Houchard
3691fb62fb0SOlivier Houchard next.generation = ck_pr_load_ptr(&head.pointer->next.generation);
3701fb62fb0SOlivier Houchard ck_pr_fence_load();
3711fb62fb0SOlivier Houchard next.pointer = ck_pr_load_ptr(&head.pointer->next.pointer);
3721fb62fb0SOlivier Houchard
3731fb62fb0SOlivier Houchard update.pointer = next.pointer;
3741fb62fb0SOlivier Houchard if (head.pointer == tail.pointer) {
3751fb62fb0SOlivier Houchard /*
3761fb62fb0SOlivier Houchard * The head is guaranteed to always point at a stub
3771fb62fb0SOlivier Houchard * entry. If the stub entry has no references then the
3781fb62fb0SOlivier Houchard * queue is empty.
3791fb62fb0SOlivier Houchard */
3801fb62fb0SOlivier Houchard if (next.pointer == NULL)
3811fb62fb0SOlivier Houchard return false;
3821fb62fb0SOlivier Houchard
3831fb62fb0SOlivier Houchard /* Forward the tail pointer if necessary. */
3841fb62fb0SOlivier Houchard update.generation = tail.generation + 1;
3851fb62fb0SOlivier Houchard ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
3861fb62fb0SOlivier Houchard } else {
3871fb62fb0SOlivier Houchard /*
3881fb62fb0SOlivier Houchard * It is possible for head snapshot to have been
3891fb62fb0SOlivier Houchard * re-used. Avoid deferencing during enqueue
3901fb62fb0SOlivier Houchard * re-use.
3911fb62fb0SOlivier Houchard */
3921fb62fb0SOlivier Houchard if (next.pointer == NULL)
3931fb62fb0SOlivier Houchard continue;
3941fb62fb0SOlivier Houchard
3951fb62fb0SOlivier Houchard /* Save value before commit. */
3961fb62fb0SOlivier Houchard *(void **)value = ck_pr_load_ptr(&next.pointer->value);
3971fb62fb0SOlivier Houchard
3981fb62fb0SOlivier Houchard /* Forward the head pointer to the next entry. */
3991fb62fb0SOlivier Houchard update.generation = head.generation + 1;
4001fb62fb0SOlivier Houchard if (ck_pr_cas_ptr_2(&fifo->head, &head, &update) == true)
4011fb62fb0SOlivier Houchard break;
4021fb62fb0SOlivier Houchard }
4031fb62fb0SOlivier Houchard }
4041fb62fb0SOlivier Houchard
4051fb62fb0SOlivier Houchard *garbage = head.pointer;
4061fb62fb0SOlivier Houchard return true;
4071fb62fb0SOlivier Houchard }
4081fb62fb0SOlivier Houchard
4091fb62fb0SOlivier Houchard CK_CC_INLINE static bool
ck_fifo_mpmc_trydequeue(struct ck_fifo_mpmc * fifo,void * value,struct ck_fifo_mpmc_entry ** garbage)4101fb62fb0SOlivier Houchard ck_fifo_mpmc_trydequeue(struct ck_fifo_mpmc *fifo,
4111fb62fb0SOlivier Houchard void *value,
4121fb62fb0SOlivier Houchard struct ck_fifo_mpmc_entry **garbage)
4131fb62fb0SOlivier Houchard {
4141fb62fb0SOlivier Houchard struct ck_fifo_mpmc_pointer head, tail, next, update;
4151fb62fb0SOlivier Houchard
4161fb62fb0SOlivier Houchard head.generation = ck_pr_load_ptr(&fifo->head.generation);
4171fb62fb0SOlivier Houchard ck_pr_fence_load();
4181fb62fb0SOlivier Houchard head.pointer = ck_pr_load_ptr(&fifo->head.pointer);
4191fb62fb0SOlivier Houchard
4201fb62fb0SOlivier Houchard tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
4211fb62fb0SOlivier Houchard ck_pr_fence_load();
4221fb62fb0SOlivier Houchard tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
4231fb62fb0SOlivier Houchard
4241fb62fb0SOlivier Houchard next.generation = ck_pr_load_ptr(&head.pointer->next.generation);
4251fb62fb0SOlivier Houchard ck_pr_fence_load();
4261fb62fb0SOlivier Houchard next.pointer = ck_pr_load_ptr(&head.pointer->next.pointer);
4271fb62fb0SOlivier Houchard
4281fb62fb0SOlivier Houchard update.pointer = next.pointer;
4291fb62fb0SOlivier Houchard if (head.pointer == tail.pointer) {
4301fb62fb0SOlivier Houchard /*
4311fb62fb0SOlivier Houchard * The head is guaranteed to always point at a stub
4321fb62fb0SOlivier Houchard * entry. If the stub entry has no references then the
4331fb62fb0SOlivier Houchard * queue is empty.
4341fb62fb0SOlivier Houchard */
4351fb62fb0SOlivier Houchard if (next.pointer == NULL)
4361fb62fb0SOlivier Houchard return false;
4371fb62fb0SOlivier Houchard
4381fb62fb0SOlivier Houchard /* Forward the tail pointer if necessary. */
4391fb62fb0SOlivier Houchard update.generation = tail.generation + 1;
4401fb62fb0SOlivier Houchard ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
4411fb62fb0SOlivier Houchard return false;
4421fb62fb0SOlivier Houchard } else {
4431fb62fb0SOlivier Houchard /*
4441fb62fb0SOlivier Houchard * It is possible for head snapshot to have been
4451fb62fb0SOlivier Houchard * re-used. Avoid deferencing during enqueue.
4461fb62fb0SOlivier Houchard */
4471fb62fb0SOlivier Houchard if (next.pointer == NULL)
4481fb62fb0SOlivier Houchard return false;
4491fb62fb0SOlivier Houchard
4501fb62fb0SOlivier Houchard /* Save value before commit. */
4511fb62fb0SOlivier Houchard *(void **)value = ck_pr_load_ptr(&next.pointer->value);
4521fb62fb0SOlivier Houchard
4531fb62fb0SOlivier Houchard /* Forward the head pointer to the next entry. */
4541fb62fb0SOlivier Houchard update.generation = head.generation + 1;
4551fb62fb0SOlivier Houchard if (ck_pr_cas_ptr_2(&fifo->head, &head, &update) == false)
4561fb62fb0SOlivier Houchard return false;
4571fb62fb0SOlivier Houchard }
4581fb62fb0SOlivier Houchard
4591fb62fb0SOlivier Houchard *garbage = head.pointer;
4601fb62fb0SOlivier Houchard return true;
4611fb62fb0SOlivier Houchard }
4621fb62fb0SOlivier Houchard
4631fb62fb0SOlivier Houchard #define CK_FIFO_MPMC_ISEMPTY(f) ((f)->head.pointer->next.pointer == NULL)
4641fb62fb0SOlivier Houchard #define CK_FIFO_MPMC_FIRST(f) ((f)->head.pointer->next.pointer)
4651fb62fb0SOlivier Houchard #define CK_FIFO_MPMC_NEXT(m) ((m)->next.pointer)
4661fb62fb0SOlivier Houchard #define CK_FIFO_MPMC_FOREACH(fifo, entry) \
4671fb62fb0SOlivier Houchard for ((entry) = CK_FIFO_MPMC_FIRST(fifo); \
4681fb62fb0SOlivier Houchard (entry) != NULL; \
4691fb62fb0SOlivier Houchard (entry) = CK_FIFO_MPMC_NEXT(entry))
4701fb62fb0SOlivier Houchard #define CK_FIFO_MPMC_FOREACH_SAFE(fifo, entry, T) \
4711fb62fb0SOlivier Houchard for ((entry) = CK_FIFO_MPMC_FIRST(fifo); \
4721fb62fb0SOlivier Houchard (entry) != NULL && ((T) = (entry)->next.pointer, 1); \
4731fb62fb0SOlivier Houchard (entry) = (T))
4741fb62fb0SOlivier Houchard
4751fb62fb0SOlivier Houchard #endif /* CK_F_FIFO_MPMC */
4761fb62fb0SOlivier Houchard #endif /* CK_F_PR_CAS_PTR_2 */
4771fb62fb0SOlivier Houchard
4781fb62fb0SOlivier Houchard #endif /* CK_FIFO_H */
479