Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/ck/include/ck_fifo.h
48255 views
1
/*
2
* Copyright 2010-2015 Samy Al Bahra.
3
* Copyright 2011 David Joseph.
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#ifndef CK_FIFO_H
29
#define CK_FIFO_H
30
31
#include <ck_cc.h>
32
#include <ck_md.h>
33
#include <ck_pr.h>
34
#include <ck_spinlock.h>
35
#include <ck_stddef.h>
36
37
#ifndef CK_F_FIFO_SPSC
38
#define CK_F_FIFO_SPSC
39
struct ck_fifo_spsc_entry {
40
void *value;
41
struct ck_fifo_spsc_entry *next;
42
};
43
typedef struct ck_fifo_spsc_entry ck_fifo_spsc_entry_t;
44
45
struct ck_fifo_spsc {
46
ck_spinlock_t m_head;
47
struct ck_fifo_spsc_entry *head;
48
char pad[CK_MD_CACHELINE - sizeof(struct ck_fifo_spsc_entry *) - sizeof(ck_spinlock_t)];
49
ck_spinlock_t m_tail;
50
struct ck_fifo_spsc_entry *tail;
51
struct ck_fifo_spsc_entry *head_snapshot;
52
struct ck_fifo_spsc_entry *garbage;
53
};
54
typedef struct ck_fifo_spsc ck_fifo_spsc_t;
55
56
CK_CC_INLINE static bool
57
ck_fifo_spsc_enqueue_trylock(struct ck_fifo_spsc *fifo)
58
{
59
60
return ck_spinlock_trylock(&fifo->m_tail);
61
}
62
63
CK_CC_INLINE static void
64
ck_fifo_spsc_enqueue_lock(struct ck_fifo_spsc *fifo)
65
{
66
67
ck_spinlock_lock(&fifo->m_tail);
68
return;
69
}
70
71
CK_CC_INLINE static void
72
ck_fifo_spsc_enqueue_unlock(struct ck_fifo_spsc *fifo)
73
{
74
75
ck_spinlock_unlock(&fifo->m_tail);
76
return;
77
}
78
79
CK_CC_INLINE static bool
80
ck_fifo_spsc_dequeue_trylock(struct ck_fifo_spsc *fifo)
81
{
82
83
return ck_spinlock_trylock(&fifo->m_head);
84
}
85
86
CK_CC_INLINE static void
87
ck_fifo_spsc_dequeue_lock(struct ck_fifo_spsc *fifo)
88
{
89
90
ck_spinlock_lock(&fifo->m_head);
91
return;
92
}
93
94
CK_CC_INLINE static void
95
ck_fifo_spsc_dequeue_unlock(struct ck_fifo_spsc *fifo)
96
{
97
98
ck_spinlock_unlock(&fifo->m_head);
99
return;
100
}
101
102
CK_CC_INLINE static void
103
ck_fifo_spsc_init(struct ck_fifo_spsc *fifo, struct ck_fifo_spsc_entry *stub)
104
{
105
106
ck_spinlock_init(&fifo->m_head);
107
ck_spinlock_init(&fifo->m_tail);
108
109
stub->next = NULL;
110
fifo->head = fifo->tail = fifo->head_snapshot = fifo->garbage = stub;
111
return;
112
}
113
114
CK_CC_INLINE static void
115
ck_fifo_spsc_deinit(struct ck_fifo_spsc *fifo, struct ck_fifo_spsc_entry **garbage)
116
{
117
118
*garbage = fifo->garbage;
119
fifo->head = fifo->tail = NULL;
120
return;
121
}
122
123
CK_CC_INLINE static void
124
ck_fifo_spsc_enqueue(struct ck_fifo_spsc *fifo,
125
struct ck_fifo_spsc_entry *entry,
126
void *value)
127
{
128
129
entry->value = value;
130
entry->next = NULL;
131
132
/* If stub->next is visible, guarantee that entry is consistent. */
133
ck_pr_fence_store();
134
ck_pr_store_ptr(&fifo->tail->next, entry);
135
fifo->tail = entry;
136
return;
137
}
138
139
CK_CC_INLINE static bool
140
ck_fifo_spsc_dequeue(struct ck_fifo_spsc *fifo, void *value)
141
{
142
struct ck_fifo_spsc_entry *entry;
143
144
/*
145
* The head pointer is guaranteed to always point to a stub entry.
146
* If the stub entry does not point to an entry, then the queue is
147
* empty.
148
*/
149
entry = ck_pr_load_ptr(&fifo->head->next);
150
if (entry == NULL)
151
return false;
152
153
/* If entry is visible, guarantee store to value is visible. */
154
ck_pr_store_ptr_unsafe(value, entry->value);
155
ck_pr_fence_store();
156
ck_pr_store_ptr(&fifo->head, entry);
157
return true;
158
}
159
160
/*
161
* Recycle a node. This technique for recycling nodes is based on
162
* Dmitriy Vyukov's work.
163
*/
164
CK_CC_INLINE static struct ck_fifo_spsc_entry *
165
ck_fifo_spsc_recycle(struct ck_fifo_spsc *fifo)
166
{
167
struct ck_fifo_spsc_entry *garbage;
168
169
if (fifo->head_snapshot == fifo->garbage) {
170
fifo->head_snapshot = ck_pr_load_ptr(&fifo->head);
171
if (fifo->head_snapshot == fifo->garbage)
172
return NULL;
173
}
174
175
garbage = fifo->garbage;
176
fifo->garbage = garbage->next;
177
return garbage;
178
}
179
180
CK_CC_INLINE static bool
181
ck_fifo_spsc_isempty(struct ck_fifo_spsc *fifo)
182
{
183
struct ck_fifo_spsc_entry *head = ck_pr_load_ptr(&fifo->head);
184
return ck_pr_load_ptr(&head->next) == NULL;
185
}
186
187
#define CK_FIFO_SPSC_ISEMPTY(f) ((f)->head->next == NULL)
188
#define CK_FIFO_SPSC_FIRST(f) ((f)->head->next)
189
#define CK_FIFO_SPSC_NEXT(m) ((m)->next)
190
#define CK_FIFO_SPSC_SPARE(f) ((f)->head)
191
#define CK_FIFO_SPSC_FOREACH(fifo, entry) \
192
for ((entry) = CK_FIFO_SPSC_FIRST(fifo); \
193
(entry) != NULL; \
194
(entry) = CK_FIFO_SPSC_NEXT(entry))
195
#define CK_FIFO_SPSC_FOREACH_SAFE(fifo, entry, T) \
196
for ((entry) = CK_FIFO_SPSC_FIRST(fifo); \
197
(entry) != NULL && ((T) = (entry)->next, 1); \
198
(entry) = (T))
199
200
#endif /* CK_F_FIFO_SPSC */
201
202
#ifdef CK_F_PR_CAS_PTR_2
203
#ifndef CK_F_FIFO_MPMC
204
#define CK_F_FIFO_MPMC
205
struct ck_fifo_mpmc_entry;
206
struct ck_fifo_mpmc_pointer {
207
struct ck_fifo_mpmc_entry *pointer;
208
char *generation CK_CC_PACKED;
209
} CK_CC_ALIGN(16);
210
211
struct ck_fifo_mpmc_entry {
212
void *value;
213
struct ck_fifo_mpmc_pointer next;
214
};
215
typedef struct ck_fifo_mpmc_entry ck_fifo_mpmc_entry_t;
216
217
struct ck_fifo_mpmc {
218
struct ck_fifo_mpmc_pointer head;
219
char pad[CK_MD_CACHELINE - sizeof(struct ck_fifo_mpmc_pointer)];
220
struct ck_fifo_mpmc_pointer tail;
221
};
222
typedef struct ck_fifo_mpmc ck_fifo_mpmc_t;
223
224
CK_CC_INLINE static void
225
ck_fifo_mpmc_init(struct ck_fifo_mpmc *fifo, struct ck_fifo_mpmc_entry *stub)
226
{
227
228
stub->next.pointer = NULL;
229
stub->next.generation = NULL;
230
fifo->head.pointer = fifo->tail.pointer = stub;
231
fifo->head.generation = fifo->tail.generation = NULL;
232
return;
233
}
234
235
CK_CC_INLINE static void
236
ck_fifo_mpmc_deinit(struct ck_fifo_mpmc *fifo, struct ck_fifo_mpmc_entry **garbage)
237
{
238
239
*garbage = fifo->head.pointer;
240
fifo->head.pointer = fifo->tail.pointer = NULL;
241
return;
242
}
243
244
CK_CC_INLINE static void
245
ck_fifo_mpmc_enqueue(struct ck_fifo_mpmc *fifo,
246
struct ck_fifo_mpmc_entry *entry,
247
void *value)
248
{
249
struct ck_fifo_mpmc_pointer tail, next, update;
250
251
/*
252
* Prepare the upcoming node and make sure to commit the updates
253
* before publishing.
254
*/
255
entry->value = value;
256
entry->next.pointer = NULL;
257
entry->next.generation = 0;
258
ck_pr_fence_store_atomic();
259
260
for (;;) {
261
tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
262
ck_pr_fence_load();
263
tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
264
next.generation = ck_pr_load_ptr(&tail.pointer->next.generation);
265
ck_pr_fence_load();
266
next.pointer = ck_pr_load_ptr(&tail.pointer->next.pointer);
267
268
if (ck_pr_load_ptr(&fifo->tail.generation) != tail.generation)
269
continue;
270
271
if (next.pointer != NULL) {
272
/*
273
* If the tail pointer has an entry following it then
274
* it needs to be forwarded to the next entry. This
275
* helps us guarantee we are always operating on the
276
* last entry.
277
*/
278
update.pointer = next.pointer;
279
update.generation = tail.generation + 1;
280
ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
281
} else {
282
/*
283
* Attempt to commit new entry to the end of the
284
* current tail.
285
*/
286
update.pointer = entry;
287
update.generation = next.generation + 1;
288
if (ck_pr_cas_ptr_2(&tail.pointer->next, &next, &update) == true)
289
break;
290
}
291
}
292
293
ck_pr_fence_atomic();
294
295
/* After a successful insert, forward the tail to the new entry. */
296
update.generation = tail.generation + 1;
297
ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
298
return;
299
}
300
301
CK_CC_INLINE static bool
302
ck_fifo_mpmc_tryenqueue(struct ck_fifo_mpmc *fifo,
303
struct ck_fifo_mpmc_entry *entry,
304
void *value)
305
{
306
struct ck_fifo_mpmc_pointer tail, next, update;
307
308
entry->value = value;
309
entry->next.pointer = NULL;
310
entry->next.generation = 0;
311
312
ck_pr_fence_store_atomic();
313
314
tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
315
ck_pr_fence_load();
316
tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
317
next.generation = ck_pr_load_ptr(&tail.pointer->next.generation);
318
ck_pr_fence_load();
319
next.pointer = ck_pr_load_ptr(&tail.pointer->next.pointer);
320
321
if (ck_pr_load_ptr(&fifo->tail.generation) != tail.generation)
322
return false;
323
324
if (next.pointer != NULL) {
325
/*
326
* If the tail pointer has an entry following it then
327
* it needs to be forwarded to the next entry. This
328
* helps us guarantee we are always operating on the
329
* last entry.
330
*/
331
update.pointer = next.pointer;
332
update.generation = tail.generation + 1;
333
ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
334
return false;
335
} else {
336
/*
337
* Attempt to commit new entry to the end of the
338
* current tail.
339
*/
340
update.pointer = entry;
341
update.generation = next.generation + 1;
342
if (ck_pr_cas_ptr_2(&tail.pointer->next, &next, &update) == false)
343
return false;
344
}
345
346
ck_pr_fence_atomic();
347
348
/* After a successful insert, forward the tail to the new entry. */
349
update.generation = tail.generation + 1;
350
ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
351
return true;
352
}
353
354
CK_CC_INLINE static bool
355
ck_fifo_mpmc_dequeue(struct ck_fifo_mpmc *fifo,
356
void *value,
357
struct ck_fifo_mpmc_entry **garbage)
358
{
359
struct ck_fifo_mpmc_pointer head, tail, next, update;
360
361
for (;;) {
362
head.generation = ck_pr_load_ptr(&fifo->head.generation);
363
ck_pr_fence_load();
364
head.pointer = ck_pr_load_ptr(&fifo->head.pointer);
365
tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
366
ck_pr_fence_load();
367
tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
368
369
next.generation = ck_pr_load_ptr(&head.pointer->next.generation);
370
ck_pr_fence_load();
371
next.pointer = ck_pr_load_ptr(&head.pointer->next.pointer);
372
373
update.pointer = next.pointer;
374
if (head.pointer == tail.pointer) {
375
/*
376
* The head is guaranteed to always point at a stub
377
* entry. If the stub entry has no references then the
378
* queue is empty.
379
*/
380
if (next.pointer == NULL)
381
return false;
382
383
/* Forward the tail pointer if necessary. */
384
update.generation = tail.generation + 1;
385
ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
386
} else {
387
/*
388
* It is possible for head snapshot to have been
389
* re-used. Avoid deferencing during enqueue
390
* re-use.
391
*/
392
if (next.pointer == NULL)
393
continue;
394
395
/* Save value before commit. */
396
*(void **)value = ck_pr_load_ptr(&next.pointer->value);
397
398
/* Forward the head pointer to the next entry. */
399
update.generation = head.generation + 1;
400
if (ck_pr_cas_ptr_2(&fifo->head, &head, &update) == true)
401
break;
402
}
403
}
404
405
*garbage = head.pointer;
406
return true;
407
}
408
409
CK_CC_INLINE static bool
410
ck_fifo_mpmc_trydequeue(struct ck_fifo_mpmc *fifo,
411
void *value,
412
struct ck_fifo_mpmc_entry **garbage)
413
{
414
struct ck_fifo_mpmc_pointer head, tail, next, update;
415
416
head.generation = ck_pr_load_ptr(&fifo->head.generation);
417
ck_pr_fence_load();
418
head.pointer = ck_pr_load_ptr(&fifo->head.pointer);
419
420
tail.generation = ck_pr_load_ptr(&fifo->tail.generation);
421
ck_pr_fence_load();
422
tail.pointer = ck_pr_load_ptr(&fifo->tail.pointer);
423
424
next.generation = ck_pr_load_ptr(&head.pointer->next.generation);
425
ck_pr_fence_load();
426
next.pointer = ck_pr_load_ptr(&head.pointer->next.pointer);
427
428
update.pointer = next.pointer;
429
if (head.pointer == tail.pointer) {
430
/*
431
* The head is guaranteed to always point at a stub
432
* entry. If the stub entry has no references then the
433
* queue is empty.
434
*/
435
if (next.pointer == NULL)
436
return false;
437
438
/* Forward the tail pointer if necessary. */
439
update.generation = tail.generation + 1;
440
ck_pr_cas_ptr_2(&fifo->tail, &tail, &update);
441
return false;
442
} else {
443
/*
444
* It is possible for head snapshot to have been
445
* re-used. Avoid deferencing during enqueue.
446
*/
447
if (next.pointer == NULL)
448
return false;
449
450
/* Save value before commit. */
451
*(void **)value = ck_pr_load_ptr(&next.pointer->value);
452
453
/* Forward the head pointer to the next entry. */
454
update.generation = head.generation + 1;
455
if (ck_pr_cas_ptr_2(&fifo->head, &head, &update) == false)
456
return false;
457
}
458
459
*garbage = head.pointer;
460
return true;
461
}
462
463
#define CK_FIFO_MPMC_ISEMPTY(f) ((f)->head.pointer->next.pointer == NULL)
464
#define CK_FIFO_MPMC_FIRST(f) ((f)->head.pointer->next.pointer)
465
#define CK_FIFO_MPMC_NEXT(m) ((m)->next.pointer)
466
#define CK_FIFO_MPMC_FOREACH(fifo, entry) \
467
for ((entry) = CK_FIFO_MPMC_FIRST(fifo); \
468
(entry) != NULL; \
469
(entry) = CK_FIFO_MPMC_NEXT(entry))
470
#define CK_FIFO_MPMC_FOREACH_SAFE(fifo, entry, T) \
471
for ((entry) = CK_FIFO_MPMC_FIRST(fifo); \
472
(entry) != NULL && ((T) = (entry)->next.pointer, 1); \
473
(entry) = (T))
474
475
#endif /* CK_F_FIFO_MPMC */
476
#endif /* CK_F_PR_CAS_PTR_2 */
477
478
#endif /* CK_FIFO_H */
479
480