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

reactor.util.concurrent.SpscLinkedArrayQueue Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

There is a newer version: 3.40.2
Show newest version
/*
 * Copyright (c) 2016-2021 VMware Inc. or its affiliates, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package reactor.util.concurrent;

import java.util.AbstractQueue;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.function.BiPredicate;

import reactor.util.annotation.Nullable;


/**
 * An unbounded, array-backed single-producer, single-consumer queue with a fixed link
 * size.
 * 

* This implementation is based on JCTools' SPSC algorithms: SpscUnboundedArrayQueue * and SpscUnboundedAtomicArrayQueue * of which the {@code SpscUnboundedAtomicArrayQueue} was contributed by one of the * authors of this library. The notable difference is that this class is not padded and * there is no lookahead cache involved; padding has a toll on short lived or bursty uses * and lookahead doesn't really matter with small queues. * * @param the value type */ final class SpscLinkedArrayQueue extends AbstractQueue implements BiPredicate { final int mask; volatile long producerIndex; @SuppressWarnings("rawtypes") static final AtomicLongFieldUpdater PRODUCER_INDEX = AtomicLongFieldUpdater.newUpdater(SpscLinkedArrayQueue.class, "producerIndex"); AtomicReferenceArray producerArray; volatile long consumerIndex; @SuppressWarnings("rawtypes") static final AtomicLongFieldUpdater CONSUMER_INDEX = AtomicLongFieldUpdater.newUpdater(SpscLinkedArrayQueue.class, "consumerIndex"); AtomicReferenceArray consumerArray; static final Object NEXT = new Object(); SpscLinkedArrayQueue(int linkSize) { int c = Queues.ceilingNextPowerOfTwo(Math.max(8, linkSize)); this.producerArray = this.consumerArray = new AtomicReferenceArray<>(c + 1); this.mask = c - 1; } @Override public boolean offer(T e) { Objects.requireNonNull(e); long pi = producerIndex; AtomicReferenceArray a = producerArray; int m = mask; int offset = (int) (pi + 1) & m; if (a.get(offset) != null) { offset = (int) pi & m; AtomicReferenceArray b = new AtomicReferenceArray<>(m + 2); producerArray = b; b.lazySet(offset, e); a.lazySet(m + 1, b); a.lazySet(offset, NEXT); PRODUCER_INDEX.lazySet(this, pi + 1); } else { offset = (int) pi & m; a.lazySet(offset, e); PRODUCER_INDEX.lazySet(this, pi + 1); } return true; } /** * Offer two elements at the same time. *

Don't use the regular offer() with this at all! * * @param first the first value, not null * @param second the second value, not null * * @return true if the queue accepted the two new values */ @Override public boolean test(T first, T second) { final AtomicReferenceArray buffer = producerArray; final long p = producerIndex; final int m = mask; int pi = (int) (p + 2) & m; if (null != buffer.get(pi)) { final AtomicReferenceArray newBuffer = new AtomicReferenceArray<>(m + 2); producerArray = newBuffer; pi = (int) p & m; newBuffer.lazySet(pi + 1, second);// StoreStore newBuffer.lazySet(pi, first); buffer.lazySet(buffer.length() - 1, newBuffer); buffer.lazySet(pi, NEXT); // new buffer is visible after element is PRODUCER_INDEX.lazySet(this, p + 2);// this ensures correctness on 32bit // platforms } else { pi = (int) p & m; buffer.lazySet(pi + 1, second); buffer.lazySet(pi, first); PRODUCER_INDEX.lazySet(this, p + 2); } return true; } @SuppressWarnings("unchecked") @Override @Nullable public T poll() { long ci = consumerIndex; AtomicReferenceArray a = consumerArray; int m = mask; int offset = (int) ci & m; Object o = a.get(offset); if (o == null) { return null; } if (o == NEXT) { AtomicReferenceArray b = (AtomicReferenceArray) a.get(m + 1); a.lazySet(m + 1, null); o = b.get(offset); a = b; consumerArray = b; } a.lazySet(offset, null); CONSUMER_INDEX.lazySet(this, ci + 1); return (T) o; } @SuppressWarnings("unchecked") @Override @Nullable public T peek() { long ci = consumerIndex; AtomicReferenceArray a = consumerArray; int m = mask; int offset = (int) ci & m; Object o = a.get(offset); if (o == null) { return null; } if (o == NEXT) { a = (AtomicReferenceArray) a.get(m + 1); o = a.get(offset); } return (T) o; } @Override public boolean isEmpty() { return producerIndex == consumerIndex; } @Override public int size() { long ci = consumerIndex; for (; ; ) { long pi = producerIndex; long ci2 = consumerIndex; if (ci == ci2) { return (int) (pi - ci); } ci = ci2; } } @Override public void clear() { while (poll() != null && !isEmpty()) { } } @Override public Iterator iterator() { throw new UnsupportedOperationException(); } }