/* -------------------------------------------------------------------------
 *
 * shmqueue.cpp
 *	  shared memory linked lists
 *
 * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/gausskernel/storage/ipc/shmqueue.cpp
 *
 * NOTES: Package for managing doubly-linked lists in shared memory.
 * The only tricky thing is that SHM_QUEUE will usually be a field
 * in a larger record.	SHMQueueNext has to return a pointer
 * to the record itself instead of a pointer to the SHMQueue field
 * of the record.  It takes an extra parameter and does some extra
 * pointer arithmetic to do this correctly.
 *
 * NOTE: These are set up so they can be turned into macros some day.
 *
 * -------------------------------------------------------------------------
 */
#include "postgres.h"
#include "knl/knl_variable.h"

#include "storage/shmem.h"

/* Make the head of a new queue point to itself */
void SHMQueueInit(SHM_QUEUE* queue)
{
    queue->prev = queue->next = queue;
}

/* return TRUE if element is not currently in a queue. */
bool SHMQueueIsDetached(const SHM_QUEUE* queue)
{
    return (queue->prev == NULL);
}

/* clear an element's links */
void SHMQueueElemInit(SHM_QUEUE* queue)
{
    queue->prev = NULL;
    queue->next = NULL;
}

/* remove an element from the queue and close the links */
void SHMQueueDelete(SHM_QUEUE* queue)
{
    SHM_QUEUE* nextElem = queue->next;
    SHM_QUEUE* prevElem = queue->prev;

    prevElem->next = queue->next;
    nextElem->prev = queue->prev;

    queue->prev = queue->next = NULL;
}

/*
 * put elem in queue before the given queue element. Inserting "before" the queue head puts the elem
 * at the tail of the queue.
 */
void SHMQueueInsertBefore(SHM_QUEUE* queue, SHM_QUEUE* elem)
{
    SHM_QUEUE* prevPtr = queue->prev;

    elem->next = prevPtr->next;
    elem->prev = queue->prev;
    queue->prev = elem;
    prevPtr->next = elem;
}

/*
 * put elem in queue after the given queue element. Inserting "after" the queue head puts the elem
 * at the head of the queue.
 */
void SHMQueueInsertAfter(SHM_QUEUE* queue, SHM_QUEUE* elem)
{
    SHM_QUEUE* nextPtr = queue->next;

    elem->prev = nextPtr->prev;
    elem->next = queue->next;
    queue->next = elem;
    nextPtr->prev = elem;
}

/**
 * Get the next element from a queue
 *
 * To start the iteration, pass the queue head as both queue and curElem.
 * Returns NULL if no more elements.
 *
 * Next element is at curElem->next.  If SHMQueue is part of
 * a larger structure, we want to return a pointer to the
 * whole structure rather than a pointer to its SHMQueue field.
 * For example,
 * struct {
 *		int				stuff;
 *		SHMQueue		elem;
 * } ELEMType;
 * When this element is in a queue, prevElem->next points at struct.elem.
 * We subtract linkOffset to get the correct start address of the structure.
 *
 * calls to SHMQueueNext should take
 * these parameters: &(queueHead), &(queueHead), offsetof(ELEMType, elem)
 * or these parameters: &(queueHead), &(curElem->elem), offsetof(ELEMType, elem)
 */
Pointer SHMQueueNext(const SHM_QUEUE* queue, const SHM_QUEUE* curElem, Size linkOffset)
{
    SHM_QUEUE* elemPtr = curElem->next;
    if (elemPtr == queue) { /* back to the queue head? */
        return NULL;
    }

    return (Pointer)(((char*)elemPtr) - linkOffset);
}

/*
 * Get the previous element from a queue
 *
 * Same as SHMQueueNext, just starting at tail and moving towards head.
 * All other comments and usage applies.
 */
Pointer SHMQueuePrev(const SHM_QUEUE* queue, const SHM_QUEUE* curElem, Size linkOffset)
{
    SHM_QUEUE* elemPtr = curElem->prev;
    if (elemPtr == queue) { /* back to the queue head? */
        return NULL;
    }

    return (Pointer)(((char*)elemPtr) - linkOffset);
}

/* return TRUE if queue head is only element, FALSE otherwise */
bool SHMQueueEmpty(const SHM_QUEUE* queue)
{
    if (queue->prev == queue) {
        Assert(queue->next == queue);
        return TRUE;
    }

    return FALSE;
}