/*! \file linuxlist_atomic.h
 *
 * Atomic versions of doubly linked list implementation.
 */

#pragma once

/*! \defgroup linuxlist Simple doubly linked list implementation
 *  @{
 * \file linuxlist.h */

#include <osmocom/core/linuxlist.h>

/*! Initialize a llist_head to point back to itself.
 *  \param[in] ptr llist_head to be initialized.
 */
#define INIT_LLIST_HEAD_AT(ptr) do { \
	CRITICAL_SECTION_ENTER();		\
	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
	CRITICAL_SECTION_LEAVE();		\
} while (0)

/*! Add a new entry into a linked list (at head).
 *  \param _new the entry to be added.
 *  \param head llist_head to prepend the element to.
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void llist_add_at(struct llist_head *_new, struct llist_head *head)
{
	CRITICAL_SECTION_ENTER()
	__llist_add(_new, head, head->next);
	CRITICAL_SECTION_LEAVE()
}

/*! Add a new entry into a linked list (at tail).
 *  \param _new the entry to be added.
 *  \param head llist_head to append the element to.
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void llist_add_tail_at(struct llist_head *_new, struct llist_head *head)
{
	CRITICAL_SECTION_ENTER()
	__llist_add(_new, head->prev, head);
	CRITICAL_SECTION_LEAVE()
}

/*! Delete a single entry from a linked list.
 *  \param entry the element to delete.
 *
 * Note: llist_empty on entry does not return true after this, the entry is
 * in an undefined state.
 */
static inline void llist_del_at(struct llist_head *entry)
{
	CRITICAL_SECTION_ENTER()
	__llist_del(entry->prev, entry->next);
	entry->next = (struct llist_head *)LLIST_POISON1;
	entry->prev = (struct llist_head *)LLIST_POISON2;
	CRITICAL_SECTION_LEAVE()
}

/*! Delete a single entry from a linked list and reinitialize it.
 *  \param entry the element to delete and reinitialize.
 */
static inline void llist_del_init_at(struct llist_head *entry)
{
	CRITICAL_SECTION_ENTER()
	__llist_del(entry->prev, entry->next);
	INIT_LLIST_HEAD(entry);
	CRITICAL_SECTION_LEAVE()
}

/*! Delete from one llist and add as another's head.
 *  \param llist the entry to move.
 *  \param head  the head that will precede our entry.
 */
static inline void llist_move_at(struct llist_head *llist, struct llist_head *head)
{
	CRITICAL_SECTION_ENTER()
	__llist_del(llist->prev, llist->next);
	llist_add(llist, head);
	CRITICAL_SECTION_LEAVE()
}

/*! Delete from one llist and add as another's tail.
 *  \param llist the entry to move.
 *  \param head  the head that will follow our entry.
 */
static inline void llist_move_tail_at(struct llist_head *llist, struct llist_head *head)
{
	CRITICAL_SECTION_ENTER()
	__llist_del(llist->prev, llist->next);
	llist_add_tail(llist, head);
	CRITICAL_SECTION_LEAVE()
}

/*! Join two linked lists.
 *  \param llist the new linked list to add.
 *  \param head  the place to add llist within the other list.
 */
static inline void llist_splice_at(struct llist_head *llist, struct llist_head *head)
{
	CRITICAL_SECTION_ENTER()
	if (!llist_empty(llist))
		__llist_splice(llist, head);
	CRITICAL_SECTION_LEAVE()
}

/*! Join two llists and reinitialise the emptied llist.
 *  \param llist the new linked list to add.
 *  \param head  the place to add it within the first llist.
 *
 * The llist is reinitialised.
 */
static inline void llist_splice_init_at(struct llist_head *llist,
				     struct llist_head *head)
{
	CRITICAL_SECTION_ENTER()
	if (!llist_empty(llist)) {
		__llist_splice(llist, head);
		INIT_LLIST_HEAD(llist);
	}
	CRITICAL_SECTION_LEAVE()
}

/*! Count number of llist items by iterating.
 *  \param head the llist head to count items of.
 *  \returns Number of items.
 *
 * This function is not efficient, mostly useful for small lists and non time
 * critical cases like unit tests.
 */
static inline unsigned int llist_count_at(const struct llist_head *head)
{
	struct llist_head *entry;
	unsigned int i = 0;
	CRITICAL_SECTION_ENTER()
	llist_for_each(entry, head)
		i++;
	CRITICAL_SECTION_LEAVE()
	return i;
}

/*!
 *  @}
 */