2019-11-05 13:28:50 -08:00
|
|
|
/*
|
|
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
|
|
*
|
2021-06-03 08:37:05 +02:00
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
|
|
*
|
2019-11-05 13:28:50 -08:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
2020-09-14 16:20:40 -07:00
|
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
2019-11-05 13:28:50 -08:00
|
|
|
*
|
|
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
|
|
* information regarding copyright ownership.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
|
|
|
#include <isc/align.h>
|
|
|
|
#include <isc/atomic.h>
|
2020-02-12 13:59:18 +01:00
|
|
|
#include <isc/hp.h>
|
|
|
|
#include <isc/mem.h>
|
2021-12-14 22:16:39 +01:00
|
|
|
#include <isc/os.h>
|
2019-11-05 13:28:50 -08:00
|
|
|
#include <isc/queue.h>
|
|
|
|
#include <isc/string.h>
|
|
|
|
|
|
|
|
#define BUFFER_SIZE 1024
|
|
|
|
|
|
|
|
static uintptr_t nulluintptr = (uintptr_t)NULL;
|
|
|
|
|
|
|
|
typedef struct node {
|
2020-02-12 13:59:18 +01:00
|
|
|
atomic_uint_fast32_t deqidx;
|
2020-02-13 14:44:37 -08:00
|
|
|
atomic_uintptr_t items[BUFFER_SIZE];
|
2020-02-12 13:59:18 +01:00
|
|
|
atomic_uint_fast32_t enqidx;
|
2020-02-13 14:44:37 -08:00
|
|
|
atomic_uintptr_t next;
|
|
|
|
isc_mem_t *mctx;
|
2019-11-05 13:28:50 -08:00
|
|
|
} node_t;
|
|
|
|
|
|
|
|
/* we just need one Hazard Pointer */
|
|
|
|
#define HP_TAIL 0
|
|
|
|
#define HP_HEAD 0
|
|
|
|
|
|
|
|
struct isc_queue {
|
2021-12-14 22:16:39 +01:00
|
|
|
alignas(ISC_OS_CACHELINE_SIZE) atomic_uintptr_t head;
|
|
|
|
alignas(ISC_OS_CACHELINE_SIZE) atomic_uintptr_t tail;
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_mem_t *mctx;
|
2020-02-13 14:44:37 -08:00
|
|
|
int taken;
|
|
|
|
isc_hp_t *hp;
|
2019-11-05 13:28:50 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
static node_t *
|
2020-02-13 14:44:37 -08:00
|
|
|
node_new(isc_mem_t *mctx, uintptr_t item) {
|
2019-11-05 13:28:50 -08:00
|
|
|
node_t *node = isc_mem_get(mctx, sizeof(*node));
|
2020-02-12 13:59:18 +01:00
|
|
|
*node = (node_t){ .mctx = NULL };
|
2019-11-05 13:28:50 -08:00
|
|
|
|
|
|
|
atomic_init(&node->deqidx, 0);
|
|
|
|
atomic_init(&node->enqidx, 1);
|
|
|
|
atomic_init(&node->next, 0);
|
|
|
|
atomic_init(&node->items[0], item);
|
|
|
|
|
|
|
|
for (int i = 1; i < BUFFER_SIZE; i++) {
|
|
|
|
atomic_init(&node->items[i], 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_mem_attach(mctx, &node->mctx);
|
|
|
|
|
|
|
|
return (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-02-13 14:44:37 -08:00
|
|
|
node_destroy(void *node0) {
|
2019-11-05 13:28:50 -08:00
|
|
|
node_t *node = (node_t *)node0;
|
|
|
|
|
|
|
|
isc_mem_putanddetach(&node->mctx, node, sizeof(*node));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2020-02-13 14:44:37 -08:00
|
|
|
node_cas_next(node_t *node, node_t *cmp, const node_t *val) {
|
2020-02-12 13:59:18 +01:00
|
|
|
return (atomic_compare_exchange_strong(&node->next, (uintptr_t *)&cmp,
|
2019-11-05 13:28:50 -08:00
|
|
|
(uintptr_t)val));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2020-02-13 14:44:37 -08:00
|
|
|
queue_cas_tail(isc_queue_t *queue, node_t *cmp, const node_t *val) {
|
2020-02-12 13:59:18 +01:00
|
|
|
return (atomic_compare_exchange_strong(&queue->tail, (uintptr_t *)&cmp,
|
2019-11-05 13:28:50 -08:00
|
|
|
(uintptr_t)val));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2020-02-13 14:44:37 -08:00
|
|
|
queue_cas_head(isc_queue_t *queue, node_t *cmp, const node_t *val) {
|
2020-02-12 13:59:18 +01:00
|
|
|
return (atomic_compare_exchange_strong(&queue->head, (uintptr_t *)&cmp,
|
2019-11-05 13:28:50 -08:00
|
|
|
(uintptr_t)val));
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_queue_t *
|
2021-12-14 22:16:39 +01:00
|
|
|
isc_queue_new(isc_mem_t *mctx) {
|
2019-11-15 10:47:08 +01:00
|
|
|
isc_queue_t *queue = NULL;
|
2020-02-13 14:44:37 -08:00
|
|
|
node_t *sentinel = NULL;
|
2019-11-05 13:28:50 -08:00
|
|
|
|
2022-01-25 23:26:27 +01:00
|
|
|
queue = isc_mem_get_aligned(mctx, sizeof(*queue), isc_os_cacheline());
|
2021-12-14 22:16:39 +01:00
|
|
|
|
|
|
|
*queue = (isc_queue_t){ 0 };
|
2019-11-05 13:28:50 -08:00
|
|
|
|
|
|
|
isc_mem_attach(mctx, &queue->mctx);
|
|
|
|
|
|
|
|
queue->hp = isc_hp_new(mctx, 1, node_destroy);
|
|
|
|
|
2019-11-15 10:47:08 +01:00
|
|
|
sentinel = node_new(mctx, nulluintptr);
|
2019-11-05 13:28:50 -08:00
|
|
|
atomic_init(&sentinel->enqidx, 0);
|
2019-11-15 10:47:08 +01:00
|
|
|
|
2019-11-05 13:28:50 -08:00
|
|
|
atomic_init(&queue->head, (uintptr_t)sentinel);
|
|
|
|
atomic_init(&queue->tail, (uintptr_t)sentinel);
|
|
|
|
|
|
|
|
return (queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_queue_enqueue(isc_queue_t *queue, uintptr_t item) {
|
2019-11-05 13:28:50 -08:00
|
|
|
REQUIRE(item != nulluintptr);
|
|
|
|
|
|
|
|
while (true) {
|
2020-02-13 14:44:37 -08:00
|
|
|
node_t *lt = NULL;
|
2019-11-05 13:28:50 -08:00
|
|
|
uint_fast32_t idx;
|
2020-02-13 14:44:37 -08:00
|
|
|
uintptr_t n = nulluintptr;
|
2019-11-05 13:28:50 -08:00
|
|
|
|
2021-12-14 22:16:39 +01:00
|
|
|
lt = (node_t *)isc_hp_protect(queue->hp, HP_TAIL, &queue->tail);
|
2019-11-05 13:28:50 -08:00
|
|
|
idx = atomic_fetch_add(<->enqidx, 1);
|
2020-02-12 13:59:18 +01:00
|
|
|
if (idx > BUFFER_SIZE - 1) {
|
2019-11-05 13:28:50 -08:00
|
|
|
node_t *lnext = NULL;
|
|
|
|
|
|
|
|
if (lt != (node_t *)atomic_load(&queue->tail)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
lnext = (node_t *)atomic_load(<->next);
|
|
|
|
if (lnext == NULL) {
|
|
|
|
node_t *newnode = node_new(queue->mctx, item);
|
|
|
|
if (node_cas_next(lt, NULL, newnode)) {
|
|
|
|
queue_cas_tail(queue, lt, newnode);
|
|
|
|
isc_hp_clear(queue->hp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
node_destroy(newnode);
|
|
|
|
} else {
|
|
|
|
queue_cas_tail(queue, lt, lnext);
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (atomic_compare_exchange_strong(<->items[idx], &n, item)) {
|
|
|
|
isc_hp_clear(queue->hp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uintptr_t
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_queue_dequeue(isc_queue_t *queue) {
|
2019-11-05 13:28:50 -08:00
|
|
|
REQUIRE(queue != NULL);
|
|
|
|
|
|
|
|
while (true) {
|
2020-02-13 14:44:37 -08:00
|
|
|
node_t *lh = NULL;
|
2019-11-05 13:28:50 -08:00
|
|
|
uint_fast32_t idx;
|
2020-02-13 14:44:37 -08:00
|
|
|
uintptr_t item;
|
2019-11-05 13:28:50 -08:00
|
|
|
|
2021-12-14 22:16:39 +01:00
|
|
|
lh = (node_t *)isc_hp_protect(queue->hp, HP_HEAD, &queue->head);
|
2019-11-05 13:28:50 -08:00
|
|
|
if (atomic_load(&lh->deqidx) >= atomic_load(&lh->enqidx) &&
|
2020-02-13 14:44:37 -08:00
|
|
|
atomic_load(&lh->next) == nulluintptr)
|
|
|
|
{
|
2019-11-05 13:28:50 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = atomic_fetch_add(&lh->deqidx, 1);
|
2020-02-12 13:59:18 +01:00
|
|
|
if (idx > BUFFER_SIZE - 1) {
|
2019-11-05 13:28:50 -08:00
|
|
|
node_t *lnext = (node_t *)atomic_load(&lh->next);
|
|
|
|
if (lnext == NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (queue_cas_head(queue, lh, lnext)) {
|
|
|
|
isc_hp_retire(queue->hp, (uintptr_t)lh);
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
item = atomic_exchange(&(lh->items[idx]),
|
|
|
|
(uintptr_t)&queue->taken);
|
|
|
|
if (item == nulluintptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_hp_clear(queue->hp);
|
|
|
|
return (item);
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_hp_clear(queue->hp);
|
|
|
|
return (nulluintptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_queue_destroy(isc_queue_t *queue) {
|
2019-11-05 13:28:50 -08:00
|
|
|
node_t *last = NULL;
|
|
|
|
|
|
|
|
REQUIRE(queue != NULL);
|
|
|
|
|
|
|
|
while (isc_queue_dequeue(queue) != nulluintptr) {
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
|
|
|
|
last = (node_t *)atomic_load_relaxed(&queue->head);
|
|
|
|
node_destroy(last);
|
|
|
|
isc_hp_destroy(queue->hp);
|
2019-11-15 10:47:08 +01:00
|
|
|
|
2021-12-14 22:16:39 +01:00
|
|
|
isc_mem_putanddetach_aligned(&queue->mctx, queue, sizeof(*queue),
|
2022-01-25 23:26:27 +01:00
|
|
|
isc_os_cacheline());
|
2019-11-05 13:28:50 -08:00
|
|
|
}
|