1309260Scognet/*
2309260Scognet * Copyright 2010-2015 Samy Al Bahra.
3309260Scognet * Copyright 2011 David Joseph.
4309260Scognet * All rights reserved.
5309260Scognet *
6309260Scognet * Redistribution and use in source and binary forms, with or without
7309260Scognet * modification, are permitted provided that the following conditions
8309260Scognet * are met:
9309260Scognet * 1. Redistributions of source code must retain the above copyright
10309260Scognet *    notice, this list of conditions and the following disclaimer.
11309260Scognet * 2. Redistributions in binary form must reproduce the above copyright
12309260Scognet *    notice, this list of conditions and the following disclaimer in the
13309260Scognet *    documentation and/or other materials provided with the distribution.
14309260Scognet *
15309260Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16309260Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17309260Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18309260Scognet * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19309260Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20309260Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21309260Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22309260Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23309260Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24309260Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25309260Scognet * SUCH DAMAGE.
26309260Scognet */
27309260Scognet
28309260Scognet#ifndef CK_FIFO_H
29309260Scognet#define CK_FIFO_H
30309260Scognet
31309260Scognet#include <ck_cc.h>
32309260Scognet#include <ck_md.h>
33309260Scognet#include <ck_pr.h>
34309260Scognet#include <ck_spinlock.h>
35309260Scognet#include <ck_stddef.h>
36309260Scognet
37309260Scognet#ifndef CK_F_FIFO_SPSC
38309260Scognet#define CK_F_FIFO_SPSC
39309260Scognetstruct ck_fifo_spsc_entry {
40309260Scognet	void *value;
41309260Scognet	struct ck_fifo_spsc_entry *next;
42309260Scognet};
43309260Scognettypedef struct ck_fifo_spsc_entry ck_fifo_spsc_entry_t;
44309260Scognet
45309260Scognetstruct ck_fifo_spsc {
46309260Scognet	ck_spinlock_t m_head;
47309260Scognet	struct ck_fifo_spsc_entry *head;
48309260Scognet	char pad[CK_MD_CACHELINE - sizeof(struct ck_fifo_spsc_entry *) - sizeof(ck_spinlock_t)];
49309260Scognet	ck_spinlock_t m_tail;
50309260Scognet	struct ck_fifo_spsc_entry *tail;
51309260Scognet	struct ck_fifo_spsc_entry *head_snapshot;
52309260Scognet	struct ck_fifo_spsc_entry *garbage;
53309260Scognet};
54309260Scognettypedef struct ck_fifo_spsc ck_fifo_spsc_t;
55309260Scognet
56309260ScognetCK_CC_INLINE static bool
57309260Scognetck_fifo_spsc_enqueue_trylock(struct ck_fifo_spsc *fifo)
58309260Scognet{
59309260Scognet
60309260Scognet	return ck_spinlock_trylock(&fifo->m_tail);
61309260Scognet}
62309260Scognet
63309260ScognetCK_CC_INLINE static void
64309260Scognetck_fifo_spsc_enqueue_lock(struct ck_fifo_spsc *fifo)
65309260Scognet{
66309260Scognet
67309260Scognet	ck_spinlock_lock(&fifo->m_tail);
68309260Scognet	return;
69309260Scognet}
70309260Scognet
71309260ScognetCK_CC_INLINE static void
72309260Scognetck_fifo_spsc_enqueue_unlock(struct ck_fifo_spsc *fifo)
73309260Scognet{
74309260Scognet
75309260Scognet	ck_spinlock_unlock(&fifo->m_tail);
76309260Scognet	return;
77309260Scognet}
78309260Scognet
79309260ScognetCK_CC_INLINE static bool
80309260Scognetck_fifo_spsc_dequeue_trylock(struct ck_fifo_spsc *fifo)
81309260Scognet{
82309260Scognet
83309260Scognet	return ck_spinlock_trylock(&fifo->m_head);
84309260Scognet}
85309260Scognet
86309260ScognetCK_CC_INLINE static void
87309260Scognetck_fifo_spsc_dequeue_lock(struct ck_fifo_spsc *fifo)
88309260Scognet{
89309260Scognet
90309260Scognet	ck_spinlock_lock(&fifo->m_head);
91309260Scognet	return;
92309260Scognet}
93309260Scognet
94309260ScognetCK_CC_INLINE static void
95309260Scognetck_fifo_spsc_dequeue_unlock(struct ck_fifo_spsc *fifo)
96309260Scognet{
97309260Scognet
98309260Scognet	ck_spinlock_unlock(&fifo->m_head);
99309260Scognet	return;
100309260Scognet}
101309260Scognet
102309260ScognetCK_CC_INLINE static void
103309260Scognetck_fifo_spsc_init(struct ck_fifo_spsc *fifo, struct ck_fifo_spsc_entry *stub)
104309260Scognet{
105309260Scognet
106309260Scognet	ck_spinlock_init(&fifo->m_head);
107309260Scognet	ck_spinlock_init(&fifo->m_tail);
108309260Scognet
109309260Scognet	stub->next = NULL;
110309260Scognet	fifo->head = fifo->tail = fifo->head_snapshot = fifo->garbage = stub;
111309260Scognet	return;
112309260Scognet}
113309260Scognet
114309260ScognetCK_CC_INLINE static void
115309260Scognetck_fifo_spsc_deinit(struct ck_fifo_spsc *fifo, struct ck_fifo_spsc_entry **garbage)
116309260Scognet{
117309260Scognet
118309260Scognet	*garbage = fifo->head;
119309260Scognet	fifo->head = fifo->tail = NULL;
120309260Scognet	return;
121309260Scognet}
122309260Scognet
123309260ScognetCK_CC_INLINE static void
124309260Scognetck_fifo_spsc_enqueue(struct ck_fifo_spsc *fifo,
125309260Scognet		     struct ck_fifo_spsc_entry *entry,
126309260Scognet		     void *value)
127309260Scognet{
128309260Scognet
129309260Scognet	entry->value = value;
130309260Scognet	entry->next = NULL;
131309260Scognet
132309260Scognet	/* If stub->next is visible, guarantee that entry is consistent. */
133309260Scognet	ck_pr_fence_store();
134309260Scognet	ck_pr_store_ptr(&fifo->tail->next, entry);
135309260Scognet	fifo->tail = entry;
136309260Scognet	return;
137309260Scognet}
138309260Scognet
139309260ScognetCK_CC_INLINE static bool
140309260Scognetck_fifo_spsc_dequeue(struct ck_fifo_spsc *fifo, void *value)
141309260Scognet{
142309260Scognet	struct ck_fifo_spsc_entry *entry;
143309260Scognet
144309260Scognet	/*
145309260Scognet	 * The head pointer is guaranteed to always point to a stub entry.
146309260Scognet	 * If the stub entry does not point to an entry, then the queue is
147309260Scognet	 * empty.
148309260Scognet	 */
149309260Scognet	entry = ck_pr_load_ptr(&fifo->head->next);
150309260Scognet	if (entry == NULL)
151309260Scognet		return false;
152309260Scognet
153309260Scognet	/* If entry is visible, guarantee store to value is visible. */
154309260Scognet	ck_pr_store_ptr_unsafe(value, entry->value);
155309260Scognet	ck_pr_fence_store();
156309260Scognet	ck_pr_store_ptr(&fifo->head, entry);
157309260Scognet	return true;
158309260Scognet}
159309260Scognet
160309260Scognet/*
161309260Scognet * Recycle a node. This technique for recycling nodes is based on
162309260Scognet * Dmitriy Vyukov's work.
163309260Scognet */
164309260ScognetCK_CC_INLINE static struct ck_fifo_spsc_entry *
165309260Scognetck_fifo_spsc_recycle(struct ck_fifo_spsc *fifo)
166309260Scognet{
167309260Scognet	struct ck_fifo_spsc_entry *garbage;
168309260Scognet
169309260Scognet	if (fifo->head_snapshot == fifo->garbage) {
170309260Scognet		fifo->head_snapshot = ck_pr_load_ptr(&fifo->head);
171309260Scognet		if (fifo->head_snapshot == fifo->garbage)
172309260Scognet			return NULL;
173309260Scognet	}
174309260Scognet
175309260Scognet	garbage = fifo->garbage;
176309260Scognet	fifo->garbage = garbage->next;
177309260Scognet	return garbage;
178309260Scognet}
179309260Scognet
180309260ScognetCK_CC_INLINE static bool
181309260Scognetck_fifo_spsc_isempty(struct ck_fifo_spsc *fifo)
182309260Scognet{
183309260Scognet	struct ck_fifo_spsc_entry *head = ck_pr_load_ptr(&fifo->head);
184309260Scognet	return ck_pr_load_ptr(&head->next) == NULL;
185309260Scognet}
186309260Scognet
187309260Scognet#define CK_FIFO_SPSC_ISEMPTY(f)	((f)->head->next == NULL)
188309260Scognet#define CK_FIFO_SPSC_FIRST(f)	((f)->head->next)
189309260Scognet#define CK_FIFO_SPSC_NEXT(m)	((m)->next)
190309260Scognet#define CK_FIFO_SPSC_SPARE(f)	((f)->head)
191309260Scognet#define CK_FIFO_SPSC_FOREACH(fifo, entry)			\
192309260Scognet	for ((entry) = CK_FIFO_SPSC_FIRST(fifo);		\
193309260Scognet	     (entry) != NULL;					\
194309260Scognet	     (entry) = CK_FIFO_SPSC_NEXT(entry))
195309260Scognet#define CK_FIFO_SPSC_FOREACH_SAFE(fifo, entry, T)		\
196309260Scognet	for ((entry) = CK_FIFO_SPSC_FIRST(fifo);		\
197309260Scognet	     (entry) != NULL && ((T) = (entry)->next, 1);	\
198309260Scognet	     (entry) = (T))
199309260Scognet
200309260Scognet#endif /* CK_F_FIFO_SPSC */
201309260Scognet
202309260Scognet#ifdef CK_F_PR_CAS_PTR_2
203309260Scognet#ifndef CK_F_FIFO_MPMC
204309260Scognet#define CK_F_FIFO_MPMC
205309260Scognetstruct ck_fifo_mpmc_entry;
206309260Scognetstruct ck_fifo_mpmc_pointer {
207309260Scognet	struct ck_fifo_mpmc_entry *pointer;
208309260Scognet	char *generation CK_CC_PACKED;
209309260Scognet} CK_CC_ALIGN(16);
210309260Scognet
211309260Scognetstruct ck_fifo_mpmc_entry {
212309260Scognet	void *value;
213309260Scognet	struct ck_fifo_mpmc_pointer next;
214309260Scognet};
215309260Scognettypedef struct ck_fifo_mpmc_entry ck_fifo_mpmc_entry_t;
216309260Scognet
217309260Scognetstruct ck_fifo_mpmc {
218309260Scognet	struct ck_fifo_mpmc_pointer head;
219309260Scognet	char pad[CK_MD_CACHELINE - sizeof(struct ck_fifo_mpmc_pointer)];
220309260Scognet	struct ck_fifo_mpmc_pointer tail;
221309260Scognet};
222309260Scognettypedef struct ck_fifo_mpmc ck_fifo_mpmc_t;
223309260Scognet
224309260ScognetCK_CC_INLINE static void
225309260Scognetck_fifo_mpmc_init(struct ck_fifo_mpmc *fifo, struct ck_fifo_mpmc_entry *stub)
226309260Scognet{
227309260Scognet
228309260Scognet	stub->next.pointer = NULL;
229309260Scognet	stub->next.generation = NULL;
230309260Scognet	fifo->head.pointer = fifo->tail.pointer = stub;
231309260Scognet	fifo->head.generation = fifo->tail.generation = NULL;
232309260Scognet	return;
233309260Scognet}
234309260Scognet
235309260ScognetCK_CC_INLINE static void
236309260Scognetck_fifo_mpmc_deinit(struct ck_fifo_mpmc *fifo, struct ck_fifo_mpmc_entry **garbage)
237309260Scognet{
238309260Scognet
239309260Scognet	*garbage = fifo->head.pointer;
240309260Scognet	fifo->head.pointer = fifo->tail.pointer = NULL;
241309260Scognet	return;
242309260Scognet}
243309260Scognet
244309260ScognetCK_CC_INLINE static void
245309260Scognetck_fifo_mpmc_enqueue(struct ck_fifo_mpmc *fifo,
246309260Scognet		     struct ck_fifo_mpmc_entry *entry,
247309260Scognet		     void *value)
248309260Scognet{
249309260Scognet	struct ck_fifo_mpmc_pointer tail, next, update;
250309260Scognet
251309260Scognet	/*
252309260Scognet	 * Prepare the upcoming node and make sure to commit the updates
253309260Scognet	 * before publishing.
254309260Scognet	 */
255309260Scognet	entry->value = value;
256309260Scognet	entry->next.pointer = NULL;
257309260Scognet	entry->next.generation = 0;
258309260Scognet	ck_pr_fence_store_atomic();
259309260Scognet
260309260Scognet	for (;;) {
261309260Scognet		tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
262309260Scognet		ck_pr_fence_load();
263309260Scognet		tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
264309260Scognet		next.generation = ck_pr_load_ptr(&tail.pointer->next.generation);
265309260Scognet		ck_pr_fence_load();
266309260Scognet		next.pointer = ck_pr_load_ptr(&tail.pointer->next.pointer);
267309260Scognet
268309260Scognet		if (ck_pr_load_ptr(&fifo->tail.generation) != tail.generation)
269309260Scognet			continue;
270309260Scognet
271309260Scognet		if (next.pointer != NULL) {
272309260Scognet			/*
273309260Scognet			 * If the tail pointer has an entry following it then
274309260Scognet			 * it needs to be forwarded to the next entry. This
275309260Scognet			 * helps us guarantee we are always operating on the
276309260Scognet			 * last entry.
277309260Scognet			 */
278309260Scognet			update.pointer = next.pointer;
279309260Scognet			update.generation = tail.generation + 1;
280309260Scognet			ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
281309260Scognet		} else {
282309260Scognet			/*
283309260Scognet			 * Attempt to commit new entry to the end of the
284309260Scognet			 * current tail.
285309260Scognet			 */
286309260Scognet			update.pointer = entry;
287309260Scognet			update.generation = next.generation + 1;
288309260Scognet			if (ck_pr_cas_ptr_2(&tail.pointer->next, &next, &update) == true)
289309260Scognet				break;
290309260Scognet		}
291309260Scognet	}
292309260Scognet
293309260Scognet	ck_pr_fence_atomic();
294309260Scognet
295309260Scognet	/* After a successful insert, forward the tail to the new entry. */
296309260Scognet	update.generation = tail.generation + 1;
297309260Scognet	ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
298309260Scognet	return;
299309260Scognet}
300309260Scognet
301309260ScognetCK_CC_INLINE static bool
302309260Scognetck_fifo_mpmc_tryenqueue(struct ck_fifo_mpmc *fifo,
303309260Scognet		        struct ck_fifo_mpmc_entry *entry,
304309260Scognet		        void *value)
305309260Scognet{
306309260Scognet	struct ck_fifo_mpmc_pointer tail, next, update;
307309260Scognet
308309260Scognet	entry->value = value;
309309260Scognet	entry->next.pointer = NULL;
310309260Scognet	entry->next.generation = 0;
311309260Scognet
312309260Scognet	ck_pr_fence_store_atomic();
313309260Scognet
314309260Scognet	tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
315309260Scognet	ck_pr_fence_load();
316309260Scognet	tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
317309260Scognet	next.generation = ck_pr_load_ptr(&tail.pointer->next.generation);
318309260Scognet	ck_pr_fence_load();
319309260Scognet	next.pointer = ck_pr_load_ptr(&tail.pointer->next.pointer);
320309260Scognet
321309260Scognet	if (ck_pr_load_ptr(&fifo->tail.generation) != tail.generation)
322309260Scognet		return false;
323309260Scognet
324309260Scognet	if (next.pointer != NULL) {
325309260Scognet		/*
326309260Scognet		 * If the tail pointer has an entry following it then
327309260Scognet		 * it needs to be forwarded to the next entry. This
328309260Scognet		 * helps us guarantee we are always operating on the
329309260Scognet		 * last entry.
330309260Scognet		 */
331309260Scognet		update.pointer = next.pointer;
332309260Scognet		update.generation = tail.generation + 1;
333309260Scognet		ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
334309260Scognet		return false;
335309260Scognet	} else {
336309260Scognet		/*
337309260Scognet		 * Attempt to commit new entry to the end of the
338309260Scognet		 * current tail.
339309260Scognet		 */
340309260Scognet		update.pointer = entry;
341309260Scognet		update.generation = next.generation + 1;
342309260Scognet		if (ck_pr_cas_ptr_2(&tail.pointer->next, &next, &update) == false)
343309260Scognet			return false;
344309260Scognet	}
345309260Scognet
346309260Scognet	ck_pr_fence_atomic();
347309260Scognet
348309260Scognet	/* After a successful insert, forward the tail to the new entry. */
349309260Scognet	update.generation = tail.generation + 1;
350309260Scognet	ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
351309260Scognet	return true;
352309260Scognet}
353309260Scognet
354309260ScognetCK_CC_INLINE static bool
355309260Scognetck_fifo_mpmc_dequeue(struct ck_fifo_mpmc *fifo,
356309260Scognet		     void *value,
357309260Scognet		     struct ck_fifo_mpmc_entry **garbage)
358309260Scognet{
359309260Scognet	struct ck_fifo_mpmc_pointer head, tail, next, update;
360309260Scognet
361309260Scognet	for (;;) {
362309260Scognet		head.generation = ck_pr_load_ptr(&fifo->head.generation);
363309260Scognet		ck_pr_fence_load();
364309260Scognet		head.pointer = ck_pr_load_ptr(&fifo->head.pointer);
365309260Scognet		tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
366309260Scognet		ck_pr_fence_load();
367309260Scognet		tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
368309260Scognet
369309260Scognet		next.generation = ck_pr_load_ptr(&head.pointer->next.generation);
370309260Scognet		ck_pr_fence_load();
371309260Scognet		next.pointer = ck_pr_load_ptr(&head.pointer->next.pointer);
372309260Scognet
373309260Scognet		update.pointer = next.pointer;
374309260Scognet		if (head.pointer == tail.pointer) {
375309260Scognet			/*
376309260Scognet			 * The head is guaranteed to always point at a stub
377309260Scognet			 * entry. If the stub entry has no references then the
378309260Scognet			 * queue is empty.
379309260Scognet			 */
380309260Scognet			if (next.pointer == NULL)
381309260Scognet				return false;
382309260Scognet
383309260Scognet			/* Forward the tail pointer if necessary. */
384309260Scognet			update.generation = tail.generation + 1;
385309260Scognet			ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
386309260Scognet		} else {
387309260Scognet			/*
388309260Scognet			 * It is possible for head snapshot to have been
389309260Scognet			 * re-used. Avoid deferencing during enqueue
390309260Scognet			 * re-use.
391309260Scognet			 */
392309260Scognet			if (next.pointer == NULL)
393309260Scognet				continue;
394309260Scognet
395309260Scognet			/* Save value before commit. */
396309260Scognet			*(void **)value = ck_pr_load_ptr(&next.pointer->value);
397309260Scognet
398309260Scognet			/* Forward the head pointer to the next entry. */
399309260Scognet			update.generation = head.generation + 1;
400309260Scognet			if (ck_pr_cas_ptr_2(&fifo->head, &head, &update) == true)
401309260Scognet				break;
402309260Scognet		}
403309260Scognet	}
404309260Scognet
405309260Scognet	*garbage = head.pointer;
406309260Scognet	return true;
407309260Scognet}
408309260Scognet
409309260ScognetCK_CC_INLINE static bool
410309260Scognetck_fifo_mpmc_trydequeue(struct ck_fifo_mpmc *fifo,
411309260Scognet			void *value,
412309260Scognet			struct ck_fifo_mpmc_entry **garbage)
413309260Scognet{
414309260Scognet	struct ck_fifo_mpmc_pointer head, tail, next, update;
415309260Scognet
416309260Scognet	head.generation = ck_pr_load_ptr(&fifo->head.generation);
417309260Scognet	ck_pr_fence_load();
418309260Scognet	head.pointer = ck_pr_load_ptr(&fifo->head.pointer);
419309260Scognet
420309260Scognet	tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
421309260Scognet	ck_pr_fence_load();
422309260Scognet	tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
423309260Scognet
424309260Scognet	next.generation = ck_pr_load_ptr(&head.pointer->next.generation);
425309260Scognet	ck_pr_fence_load();
426309260Scognet	next.pointer = ck_pr_load_ptr(&head.pointer->next.pointer);
427309260Scognet
428309260Scognet	update.pointer = next.pointer;
429309260Scognet	if (head.pointer == tail.pointer) {
430309260Scognet		/*
431309260Scognet		 * The head is guaranteed to always point at a stub
432309260Scognet		 * entry. If the stub entry has no references then the
433309260Scognet		 * queue is empty.
434309260Scognet		 */
435309260Scognet		if (next.pointer == NULL)
436309260Scognet			return false;
437309260Scognet
438309260Scognet		/* Forward the tail pointer if necessary. */
439309260Scognet		update.generation = tail.generation + 1;
440309260Scognet		ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
441309260Scognet		return false;
442309260Scognet	} else {
443309260Scognet		/*
444309260Scognet		 * It is possible for head snapshot to have been
445309260Scognet		 * re-used. Avoid deferencing during enqueue.
446309260Scognet		 */
447309260Scognet		if (next.pointer == NULL)
448309260Scognet			return false;
449309260Scognet
450309260Scognet		/* Save value before commit. */
451309260Scognet		*(void **)value = ck_pr_load_ptr(&next.pointer->value);
452309260Scognet
453309260Scognet		/* Forward the head pointer to the next entry. */
454309260Scognet		update.generation = head.generation + 1;
455309260Scognet		if (ck_pr_cas_ptr_2(&fifo->head, &head, &update) == false)
456309260Scognet			return false;
457309260Scognet	}
458309260Scognet
459309260Scognet	*garbage = head.pointer;
460309260Scognet	return true;
461309260Scognet}
462309260Scognet
463309260Scognet#define CK_FIFO_MPMC_ISEMPTY(f)	((f)->head.pointer->next.pointer == NULL)
464309260Scognet#define CK_FIFO_MPMC_FIRST(f)	((f)->head.pointer->next.pointer)
465309260Scognet#define CK_FIFO_MPMC_NEXT(m)	((m)->next.pointer)
466309260Scognet#define CK_FIFO_MPMC_FOREACH(fifo, entry)				\
467309260Scognet	for ((entry) = CK_FIFO_MPMC_FIRST(fifo);			\
468309260Scognet	     (entry) != NULL;						\
469309260Scognet	     (entry) = CK_FIFO_MPMC_NEXT(entry))
470309260Scognet#define CK_FIFO_MPMC_FOREACH_SAFE(fifo, entry, T)			\
471309260Scognet	for ((entry) = CK_FIFO_MPMC_FIRST(fifo);			\
472309260Scognet	     (entry) != NULL && ((T) = (entry)->next.pointer, 1);	\
473309260Scognet	     (entry) = (T))
474309260Scognet
475309260Scognet#endif /* CK_F_FIFO_MPMC */
476309260Scognet#endif /* CK_F_PR_CAS_PTR_2 */
477309260Scognet
478309260Scognet#endif /* CK_FIFO_H */
479