All Downloads are FREE. Search and download functionalities are using the official Maven repository.

zmq.pipe.YQueue Maven / Gradle / Ivy

The newest version!
package zmq.pipe;

class YQueue
{
    //  Individual memory chunk to hold N elements.
    private static class Chunk
    {
        final T[]   values;
        final int[] pos;
        Chunk    prev;
        Chunk    next;

        @SuppressWarnings("unchecked")
        public Chunk(int size, int memoryPtr)
        {
            values = (T[]) new Object[size];
            pos = new int[size];
            for (int i = 0; i != values.length; i++) {
                pos[i] = memoryPtr;
                memoryPtr++;
            }
        }
    }

    //  Back position may point to invalid memory if the queue is empty,
    //  while begin & end positions are always valid. Begin position is
    //  accessed exclusively be queue reader (front/pop), while back and
    //  end positions are accessed exclusively by queue writer (back/push).
    private Chunk          beginChunk;
    private int               beginPos;
    private Chunk          backChunk;
    private int               backPos;
    private Chunk          endChunk;
    private int               endPos;
    private volatile Chunk spareChunk;
    private final int         size;

    //  People are likely to produce and consume at similar rates.  In
    //  this scenario holding onto the most recently freed chunk saves
    //  us from having to call malloc/free.
    private int memoryPtr;

    public YQueue(int size)
    {
        this.size = size;
        memoryPtr = 0;
        beginChunk = new Chunk<>(size, memoryPtr);
        memoryPtr += size;
        beginPos = 0;
        backPos = 0;
        backChunk = beginChunk;
        spareChunk = beginChunk;
        endChunk = beginChunk;
        endPos = 1;
    }

    public int frontPos()
    {
        return beginChunk.pos[beginPos];
    }

    //  Returns reference to the front element of the queue.
    //  If the queue is empty, behaviour is undefined.
    public T front()
    {
        return beginChunk.values[beginPos];
    }

    public int backPos()
    {
        return backChunk.pos[backPos];
    }

    //  Returns reference to the back element of the queue.
    //  If the queue is empty, behaviour is undefined.
    public T back()
    {
        return backChunk.values[backPos];
    }

    //  Adds an element to the back end of the queue.
    public void push(T val)
    {
        backChunk.values[backPos] = val;
        backChunk = endChunk;
        backPos = endPos;

        if (++endPos != size) {
            return;
        }

        Chunk sc = spareChunk;
        if (sc != beginChunk) {
            spareChunk = spareChunk.next;
            endChunk.next = sc;
            sc.prev = endChunk;
        }
        else {
            endChunk.next = new Chunk<>(size, memoryPtr);
            memoryPtr += size;
            endChunk.next.prev = endChunk;
        }
        endChunk = endChunk.next;
        endPos = 0;
    }

    //  Removes element from the back end of the queue. In other words
    //  it rollbacks last push to the queue. Take care: Caller is
    //  responsible for destroying the object being unpushed.
    //  The caller must also guarantee that the queue isn't empty when
    //  unpush is called. It cannot be done automatically as the read
    //  side of the queue can be managed by different, completely
    //  unsynchronised thread.
    public void unpush()
    {
        //  First, move 'back' one position backwards.
        if (backPos > 0) {
            --backPos;
        }
        else {
            backPos = size - 1;
            backChunk = backChunk.prev;
        }

        //  Now, move 'end' position backwards. Note that obsolete end chunk
        //  is not used as a spare chunk. The analysis shows that doing so
        //  would require free and atomic operation per chunk deallocated
        //  instead of a simple free.
        if (endPos > 0) {
            --endPos;
        }
        else {
            endPos = size - 1;
            endChunk = endChunk.prev;
            endChunk.next = null;
        }
    }

    //  Removes an element from the front end of the queue.
    public T pop()
    {
        T val = beginChunk.values[beginPos];
        beginChunk.values[beginPos] = null;
        beginPos++;
        if (beginPos == size) {
            beginChunk = beginChunk.next;
            beginChunk.prev = null;
            beginPos = 0;
        }
        return val;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy