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

co.paralleluniverse.strands.queues.CircularBuffer Maven / Gradle / Ivy

Go to download

The core library for Fibers on Java, compatible with Java 11-16. Forked from puniverse/quasar

There is a newer version: 10.0.6
Show newest version
/*
 * Quasar: lightweight threads and actors for the JVM.
 * Copyright (c) 2013-2015, Parallel Universe Software Co. All rights reserved.
 * 
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *  
 *   or (per the licensee's choosing)
 *  
 * under the terms of the GNU Lesser General Public License version 3.0
 * as published by the Free Software Foundation.
 */
package co.paralleluniverse.strands.queues;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

/**
 *
 * @author pron
 */
public abstract class CircularBuffer implements BasicQueue {
    final int capacity;
    final int mask;
    private final boolean singleProducer;
    volatile long p101, p102, p103, p104, p105, p106, p107;
    volatile long tail; // next element to be written
    volatile long p201, p202, p203, p204, p205, p206, p207;
    volatile long lastWritten; // follows tail
    volatile Object p301, p302, p303, p304, p305, p306, p307;
    final Consumer consumer;

    CircularBuffer(int capacity, boolean singleProducer) {
        // capacity is a power of 2
        this.capacity = nextPowerOfTwo(capacity);
        this.mask = this.capacity - 1;
        this.singleProducer = singleProducer;
        this.consumer = newConsumer();
    }

    public boolean isSingleProducer() {
        return singleProducer;
    }

    public Consumer builtinConsumer() {
        return consumer;
    }

    private static int nextPowerOfTwo(int v) {
        assert v >= 0;
        return 1 << (32 - Integer.numberOfLeadingZeros(v - 1));
    }

    @Override
    public int capacity() {
        return capacity;
    }

    final long preEnq() {
        long t;

        if (singleProducer) {
            t = tail;
            tail++; // orderedSetTail(t + 1); // 
        } else {
            do {
                t = tail;
            } while (!casTail(t, t + 1));
        }
        return t;
    }

    final void postEnq() {
        if (singleProducer)
            lastWritten++;
        else {
            long w;
            do {
                w = lastWritten;
            } while (!casLastWritten(w, w + 1));
        }
    }

    @Override
    public abstract boolean enq(E elem);

    @Override
    public E poll() {
        return consumer.poll();
    }

    @Override
    public int size() {
        return consumer.size();
    }

    @Override
    public boolean isEmpty() {
        return consumer.isEmpty();
    }

    public boolean hasNext() {
        return consumer.hasNext();
    }

    public abstract Consumer newConsumer();

    public abstract class Consumer {
        long head;

        public final long lastIndexRead() {
            return head - 1;
        }

        public final boolean hasNext() {
            return tail > head;
        }

        public void poll0() {
            assert tail > head;
            int headStart = 0; // how many elements ahead of tail we'll try to read if tail progresses too fast
            int attempt = 0;
            for (;;) {
                while (lastWritten <= head) // wait for enq to complete 
                        ;
                grabValue((int) head & mask);
                final long oldest = tail - capacity;
                if (head >= oldest) {
                    head++;
                    return;
                }
                // tail has overtaken us
                head = oldest + headStart; // < tail
                if (attempt > 30)
                    throw new RuntimeException("Can't catch up with producer");
                // increasing headStart diesn't work for some reason. it breaks monotonicity somehow...
//                if ((++attempt & 0x03) == 0) { // every 4 attempts inc headStart
//                    headStart++;
//                    if (headStart >= capacity)
//                        throw new RuntimeException("Can't catch up with producer");
//                }
                attempt++;
            }
        }

        public E poll() {
            if (!hasNext())
                return null;
            poll0();
            final E v = getValue();
            clearValue(); // for gc
            return v;
        }

        public E getAndClearReadValue() {
            final E v = getValue();
            clearValue(); // for gc
            return v;
        }

        public int size() {
            return (int) (tail - head);
        }

        public boolean isEmpty() {
            return tail == head;
        }

        protected abstract void grabValue(int index);

        protected abstract void clearValue();

        protected abstract E getValue();
    }
    ////////////////////////////////////////////////////////////////////////
    private static final VarHandle TAIL;
    private static final VarHandle LAST_WRITTEN;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            TAIL = l.findVarHandle(CircularBuffer.class, "tail", long.class);
            LAST_WRITTEN = l.findVarHandle(CircularBuffer.class, "lastWritten", long.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private void orderedSetTail(long value) {
        TAIL.setOpaque(this, value); // UNSAFE.putOrderedLong(this, tailOffset, value);
    }

    boolean casTail(long expected, long update) {
        return TAIL.compareAndSet(this, expected, update);
    }

    boolean casLastWritten(long expected, long update) {
        return LAST_WRITTEN.compareAndSet(this, expected, update);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy