|
|
|
/* Copyright (C) 2021,2022 fef <owo@fef.moe>. All rights reserved. */
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <arch/atom.h>
|
|
|
|
|
|
|
|
#include <gay/mm.h>
|
|
|
|
#include <gay/types.h>
|
|
|
|
#include <gay/util.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @defgroup kqueue Lock Free FIFO (kqueue)
|
|
|
|
*
|
|
|
|
* kqueue is a simple API that allows any structure to become part of a
|
|
|
|
* lock-free FIFO queue. It only uses atomic primitives and spin-wait loops,
|
|
|
|
* which makes it safe to use even from irq context. Furthermore, it supports
|
|
|
|
* multiple concurrent emitters *and* consumers, making it suitable for stuff
|
|
|
|
* like serving as the backend for a basic batch processing system distributed
|
|
|
|
* across several CPUs.
|
|
|
|
*
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @brief Embed this into structs that you want to put in a kqueue. */
|
|
|
|
struct kq_node {
|
|
|
|
patom_t next;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** @brief Lock free FIFO with support for multiple emitters and consumers. */
|
|
|
|
struct kqueue {
|
|
|
|
patom_t head;
|
|
|
|
patom_t tail;
|
|
|
|
struct kq_node _dummy;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Initialize a kqueue.
|
|
|
|
*
|
|
|
|
* @param kq kqueue to initialize
|
|
|
|
*/
|
|
|
|
static inline void kq_init(struct kqueue *kq)
|
|
|
|
{
|
|
|
|
patom_init(&kq->head, &kq->_dummy);
|
|
|
|
patom_init(&kq->tail, &kq->_dummy);
|
|
|
|
patom_init(&kq->_dummy.next, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Allocate a new kqueue and initialize it.
|
|
|
|
*
|
|
|
|
* @param flags Flags for `kmalloc()`
|
|
|
|
* @return The initialized kqueue, or `nil` if OOM
|
|
|
|
*/
|
|
|
|
struct kqueue *kq_create(enum mflags flags);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Add a new entry to the end of a kqueue.
|
|
|
|
*
|
|
|
|
* @param kq kqueue to append the entry to
|
|
|
|
* @param node New node to append to the queue
|
|
|
|
*/
|
|
|
|
void kq_add(struct kqueue *kq, struct kq_node *node);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Remove the first item in a kqueue.
|
|
|
|
*
|
|
|
|
* @param kq kqueue to remove an item from
|
|
|
|
* @return The removed item, or `nil` if the queue was empty
|
|
|
|
*/
|
|
|
|
struct kq_node *kq_del(struct kqueue *kq);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Cast a kqueue entry out to the structure it is embedded in.
|
|
|
|
*
|
|
|
|
* @param head The `kq_node_t *` to cast out of
|
|
|
|
* @param type Type of the containing structure
|
|
|
|
* @param member Name of the `kq_node_t` within the structure
|
|
|
|
* @returns The `struct *` that `head` is embedded in
|
|
|
|
*/
|
|
|
|
#define kq_entry(head, type, member) container_of(head, type, member)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Remove the first item in a kqueue and return the structure
|
|
|
|
* it is embedded in.
|
|
|
|
*
|
|
|
|
* @param kq kqueue to remove an item from
|
|
|
|
* @param type Type of the containing structure
|
|
|
|
* @param member Name of the `kq_node_t` within the structure
|
|
|
|
* @returns The `struct *` that was removed from the queue,
|
|
|
|
* or `nil` if the queue was empty
|
|
|
|
*/
|
|
|
|
#define kq_del_entry(kq, type, member) ({ \
|
|
|
|
struct kq_node *__node = kq_del(kq); \
|
|
|
|
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
|
|
|
|
type *__entry = nil; \
|
|
|
|
if (__node != nil) \
|
|
|
|
__entry = kq_entry(__node, type, member); \
|
|
|
|
__entry; \
|
|
|
|
})
|
|
|
|
|
|
|
|
/** @} */
|