@Override
 public T poll() {
   long ci = consumerIndex;
   int offset = (int) ci & mask;
   T value = get(offset);
   if (value == null) {
     return null;
   }
   CONSUMER_INDEX.lazySet(this, ci + 1);
   lazySet(offset, null);
   return value;
 }
예제 #2
0
 protected void downstreamRequest(long n) {
   for (; ; ) {
     long r = requested;
     long u;
     if (r != Long.MAX_VALUE && n == Long.MAX_VALUE) {
       u = Long.MAX_VALUE;
     } else if (r + n < 0) {
       u = Long.MAX_VALUE;
     } else {
       u = r + n;
     }
     if (REQUESTED.compareAndSet(this, r, u)) {
       break;
     }
   }
   drain();
 }
  @Override
  public boolean offer(T value) {
    if (value == null) {
      throw new NullPointerException();
    }

    long pi = producerIndex;
    int m = mask;

    int fullCheck = (int) (pi + capacitySkip) & m;
    if (get(fullCheck) != null) {
      return false;
    }
    int offset = (int) pi & m;
    PRODUCER_INDEX.lazySet(this, pi + 1);
    lazySet(offset, value);
    return true;
  }
/**
 * A single-producer single-consumer bounded queue with exact capacity tracking.
 *
 * <p>This means that a queue of 10 will allow exactly 10 offers, however, the underlying storage is
 * still power-of-2.
 *
 * <p>The implementation uses field updaters and thus should be platform-safe.
 */
public final class SpscExactAtomicArrayQueue<T> extends AtomicReferenceArray<T>
    implements Queue<T> {
  /** */
  private static final long serialVersionUID = 6210984603741293445L;

  final int mask;
  final int capacitySkip;
  volatile long producerIndex;
  volatile long consumerIndex;

  @SuppressWarnings("rawtypes")
  static final AtomicLongFieldUpdater<SpscExactAtomicArrayQueue> PRODUCER_INDEX =
      AtomicLongFieldUpdater.newUpdater(SpscExactAtomicArrayQueue.class, "producerIndex");

  @SuppressWarnings("rawtypes")
  static final AtomicLongFieldUpdater<SpscExactAtomicArrayQueue> CONSUMER_INDEX =
      AtomicLongFieldUpdater.newUpdater(SpscExactAtomicArrayQueue.class, "consumerIndex");

  public SpscExactAtomicArrayQueue(int capacity) {
    super(Pow2.roundToPowerOfTwo(capacity));
    int len = length();
    this.mask = len - 1;
    this.capacitySkip = len - capacity;
  }

  @Override
  public boolean offer(T value) {
    if (value == null) {
      throw new NullPointerException();
    }

    long pi = producerIndex;
    int m = mask;

    int fullCheck = (int) (pi + capacitySkip) & m;
    if (get(fullCheck) != null) {
      return false;
    }
    int offset = (int) pi & m;
    PRODUCER_INDEX.lazySet(this, pi + 1);
    lazySet(offset, value);
    return true;
  }

  @Override
  public T poll() {
    long ci = consumerIndex;
    int offset = (int) ci & mask;
    T value = get(offset);
    if (value == null) {
      return null;
    }
    CONSUMER_INDEX.lazySet(this, ci + 1);
    lazySet(offset, null);
    return value;
  }

  @Override
  public T peek() {
    return get((int) consumerIndex & mask);
  }

  @Override
  public void clear() {
    while (poll() != null || !isEmpty()) ;
  }

  @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 - ci2);
      }
      ci = ci2;
    }
  }

  @Override
  public boolean contains(Object o) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Iterator<T> iterator() {
    throw new UnsupportedOperationException();
  }

  @Override
  public Object[] toArray() {
    throw new UnsupportedOperationException();
  }

  @Override
  public <E> E[] toArray(E[] a) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean remove(Object o) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean containsAll(Collection<?> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean addAll(Collection<? extends T> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean removeAll(Collection<?> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean retainAll(Collection<?> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean add(T e) {
    throw new UnsupportedOperationException();
  }

  @Override
  public T remove() {
    throw new UnsupportedOperationException();
  }

  @Override
  public T element() {
    throw new UnsupportedOperationException();
  }
}
/**
 * A single-producer single-consumer array-backed queue which can allocate new arrays in case the
 * consumer is slower than the producer.
 */
public final class SpscLinkedArrayQueue<T> implements Queue<T> {
  static final int MAX_LOOK_AHEAD_STEP =
      Integer.getInteger("jctools.spsc.max.lookahead.step", 4096);
  protected volatile long producerIndex;

  @SuppressWarnings("rawtypes")
  static final AtomicLongFieldUpdater<SpscLinkedArrayQueue> PRODUCER_INDEX =
      AtomicLongFieldUpdater.newUpdater(SpscLinkedArrayQueue.class, "producerIndex");

  protected int producerLookAheadStep;
  protected long producerLookAhead;
  protected int producerMask;
  protected AtomicReferenceArray<Object> producerBuffer;
  protected int consumerMask;
  protected AtomicReferenceArray<Object> consumerBuffer;
  protected volatile long consumerIndex;

  @SuppressWarnings("rawtypes")
  static final AtomicLongFieldUpdater<SpscLinkedArrayQueue> CONSUMER_INDEX =
      AtomicLongFieldUpdater.newUpdater(SpscLinkedArrayQueue.class, "consumerIndex");

  private static final Object HAS_NEXT = new Object();

  public SpscLinkedArrayQueue(final int bufferSize) {
    int p2capacity = Pow2.roundToPowerOfTwo(bufferSize);
    int mask = p2capacity - 1;
    AtomicReferenceArray<Object> buffer = new AtomicReferenceArray<>(p2capacity + 1);
    producerBuffer = buffer;
    producerMask = mask;
    adjustLookAheadStep(p2capacity);
    consumerBuffer = buffer;
    consumerMask = mask;
    producerLookAhead = mask - 1; // we know it's all empty to start with
    soProducerIndex(0L);
  }

  /**
   * {@inheritDoc}
   *
   * <p>This implementation is correct for single producer thread use only.
   */
  @Override
  public final boolean offer(final T e) {
    // local load of field to avoid repeated loads after volatile reads
    final AtomicReferenceArray<Object> buffer = producerBuffer;
    final long index = lpProducerIndex();
    final int mask = producerMask;
    final int offset = calcWrappedOffset(index, mask);
    if (index < producerLookAhead) {
      return writeToQueue(buffer, e, index, offset);
    } else {
      final int lookAheadStep = producerLookAheadStep;
      // go around the buffer or resize if full (unless we hit max capacity)
      int lookAheadElementOffset = calcWrappedOffset(index + lookAheadStep, mask);
      if (null == lvElement(buffer, lookAheadElementOffset)) { // LoadLoad
        producerLookAhead = index + lookAheadStep - 1; // joy, there's plenty of room
        return writeToQueue(buffer, e, index, offset);
      } else if (null
          == lvElement(buffer, calcWrappedOffset(index + 1, mask))) { // buffer is not full
        return writeToQueue(buffer, e, index, offset);
      } else {
        resize(buffer, index, offset, e, mask); // add a buffer and link old to new
        return true;
      }
    }
  }

  private boolean writeToQueue(
      final AtomicReferenceArray<Object> buffer, final T e, final long index, final int offset) {
    soProducerIndex(index + 1); // this ensures atomic write of long on 32bit platforms
    soElement(buffer, offset, e); // StoreStore
    return true;
  }

  private void resize(
      final AtomicReferenceArray<Object> oldBuffer,
      final long currIndex,
      final int offset,
      final T e,
      final long mask) {
    final int capacity = oldBuffer.length();
    final AtomicReferenceArray<Object> newBuffer = new AtomicReferenceArray<>(capacity);
    producerBuffer = newBuffer;
    producerLookAhead = currIndex + mask - 1;
    soProducerIndex(currIndex + 1); // this ensures correctness on 32bit platforms
    soElement(newBuffer, offset, e); // StoreStore
    soNext(oldBuffer, newBuffer);
    soElement(oldBuffer, offset, HAS_NEXT); // new buffer is visible after element is
    // inserted
  }

  private void soNext(AtomicReferenceArray<Object> curr, AtomicReferenceArray<Object> next) {
    soElement(curr, calcDirectOffset(curr.length() - 1), next);
  }

  @SuppressWarnings("unchecked")
  private AtomicReferenceArray<Object> lvNext(AtomicReferenceArray<Object> curr) {
    return (AtomicReferenceArray<Object>) lvElement(curr, calcDirectOffset(curr.length() - 1));
  }
  /**
   * {@inheritDoc}
   *
   * <p>This implementation is correct for single consumer thread use only.
   */
  @SuppressWarnings("unchecked")
  @Override
  public final T poll() {
    // local load of field to avoid repeated loads after volatile reads
    final AtomicReferenceArray<Object> buffer = consumerBuffer;
    final long index = lpConsumerIndex();
    final int mask = consumerMask;
    final int offset = calcWrappedOffset(index, mask);
    final Object e = lvElement(buffer, offset); // LoadLoad
    boolean isNextBuffer = e == HAS_NEXT;
    if (null != e && !isNextBuffer) {
      soConsumerIndex(index + 1); // this ensures correctness on 32bit platforms
      soElement(buffer, offset, null); // StoreStore
      return (T) e;
    } else if (isNextBuffer) {
      return newBufferPoll(lvNext(buffer), index, mask);
    }

    return null;
  }

  @SuppressWarnings("unchecked")
  private T newBufferPoll(
      AtomicReferenceArray<Object> nextBuffer, final long index, final int mask) {
    consumerBuffer = nextBuffer;
    final int offsetInNew = calcWrappedOffset(index, mask);
    final T n = (T) lvElement(nextBuffer, offsetInNew); // LoadLoad
    if (null == n) {
      return null;
    } else {
      soConsumerIndex(index + 1); // this ensures correctness on 32bit platforms
      soElement(nextBuffer, offsetInNew, null); // StoreStore
      return n;
    }
  }

  /**
   * {@inheritDoc}
   *
   * <p>This implementation is correct for single consumer thread use only.
   */
  @SuppressWarnings("unchecked")
  @Override
  public final T peek() {
    final AtomicReferenceArray<Object> buffer = consumerBuffer;
    final long index = lpConsumerIndex();
    final int mask = consumerMask;
    final int offset = calcWrappedOffset(index, mask);
    final Object e = lvElement(buffer, offset); // LoadLoad
    if (e == HAS_NEXT) {
      return newBufferPeek(lvNext(buffer), index, mask);
    }

    return (T) e;
  }

  @Override
  public void clear() {
    while (poll() != null || !isEmpty()) ;
  }

  @SuppressWarnings("unchecked")
  private T newBufferPeek(
      AtomicReferenceArray<Object> nextBuffer, final long index, final int mask) {
    consumerBuffer = nextBuffer;
    final int offsetInNew = calcWrappedOffset(index, mask);
    return (T) lvElement(nextBuffer, offsetInNew); // LoadLoad
  }

  @Override
  public final int size() {
    /*
     * It is possible for a thread to be interrupted or reschedule between the read of the producer and
     * consumer indices, therefore protection is required to ensure size is within valid range. In the
     * event of concurrent polls/offers to this method the size is OVER estimated as we read consumer
     * index BEFORE the producer index.
     */
    long after = lvConsumerIndex();
    while (true) {
      final long before = after;
      final long currentProducerIndex = lvProducerIndex();
      after = lvConsumerIndex();
      if (before == after) {
        return (int) (currentProducerIndex - after);
      }
    }
  }

  @Override
  public boolean isEmpty() {
    return lvProducerIndex() == lvConsumerIndex();
  }

  private void adjustLookAheadStep(int capacity) {
    producerLookAheadStep = Math.min(capacity / 4, MAX_LOOK_AHEAD_STEP);
  }

  private long lvProducerIndex() {
    return producerIndex;
  }

  private long lvConsumerIndex() {
    return consumerIndex;
  }

  private long lpProducerIndex() {
    return producerIndex;
  }

  private long lpConsumerIndex() {
    return consumerIndex;
  }

  private void soProducerIndex(long v) {
    PRODUCER_INDEX.lazySet(this, v);
  }

  private void soConsumerIndex(long v) {
    CONSUMER_INDEX.lazySet(this, v);
  }

  private static final int calcWrappedOffset(long index, int mask) {
    return calcDirectOffset((int) index & mask);
  }

  private static final int calcDirectOffset(int index) {
    return index;
  }

  private static final void soElement(AtomicReferenceArray<Object> buffer, int offset, Object e) {
    buffer.lazySet(offset, e);
  }

  private static final <E> Object lvElement(AtomicReferenceArray<Object> buffer, int offset) {
    return buffer.get(offset);
  }

  @Override
  public final Iterator<T> iterator() {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean contains(Object o) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Object[] toArray() {
    throw new UnsupportedOperationException();
  }

  @Override
  public <E> E[] toArray(E[] a) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean remove(Object o) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean containsAll(Collection<?> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean addAll(Collection<? extends T> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean removeAll(Collection<?> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean retainAll(Collection<?> c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean add(T e) {
    throw new UnsupportedOperationException();
  }

  @Override
  public T remove() {
    throw new UnsupportedOperationException();
  }

  @Override
  public T element() {
    throw new UnsupportedOperationException();
  }

  /**
   * Offer two elements at the same time.
   *
   * <p>Don't use the regular offer() with this at all!
   *
   * @param first
   * @param second
   * @return
   */
  public boolean offer(T first, T second) {
    final AtomicReferenceArray<Object> buffer = producerBuffer;
    final long p = producerIndex;
    final int m = producerMask;

    int pi = calcWrappedOffset(p + 2, m);

    if (null == lvElement(buffer, pi)) {
      pi = calcWrappedOffset(p, m);
      soElement(buffer, pi + 1, second);
      soProducerIndex(p + 2);
      soElement(buffer, pi, first);
    } else {
      final int capacity = buffer.length();
      final AtomicReferenceArray<Object> newBuffer = new AtomicReferenceArray<>(capacity);
      producerBuffer = newBuffer;

      pi = calcWrappedOffset(p, m);
      soElement(newBuffer, pi + 1, second); // StoreStore
      soElement(newBuffer, pi, first);
      soNext(buffer, newBuffer);

      soProducerIndex(p + 2); // this ensures correctness on 32bit platforms

      soElement(buffer, pi, HAS_NEXT); // new buffer is visible after element is
    }

    return true;
  }
}
 private void soConsumerIndex(long v) {
   CONSUMER_INDEX.lazySet(this, v);
 }
 private void soProducerIndex(long v) {
   PRODUCER_INDEX.lazySet(this, v);
 }
예제 #8
0
  static final class SourceSubscriber<T> extends Subscriber<Observable<? extends T>> {
    final NotificationLite<T> nl = NotificationLite.instance();
    final int maxConcurrency;
    final Subscriber<T> s;
    final CompositeSubscription csub;
    final Object guard;

    volatile int wip;

    @SuppressWarnings("rawtypes")
    static final AtomicIntegerFieldUpdater<SourceSubscriber> WIP =
        AtomicIntegerFieldUpdater.newUpdater(SourceSubscriber.class, "wip");

    volatile int sourceIndex;

    @SuppressWarnings("rawtypes")
    static final AtomicIntegerFieldUpdater<SourceSubscriber> SOURCE_INDEX =
        AtomicIntegerFieldUpdater.newUpdater(SourceSubscriber.class, "sourceIndex");

    /** Guarded by guard. */
    int active;
    /** Guarded by guard. */
    final Queue<Observable<? extends T>> queue;

    /** Indicates the emitting phase. Guarded by this. */
    boolean emitting;
    /** Counts the missed emitting calls. Guarded by this. */
    int missedEmitting;
    /** The last buffer index in the round-robin drain scheme. Accessed while emitting == true. */
    int lastIndex;

    /** Guarded by itself. */
    final List<MergeItemSubscriber> subscribers;

    volatile long requested;

    @SuppressWarnings("rawtypes")
    static final AtomicLongFieldUpdater<SourceSubscriber> REQUESTED =
        AtomicLongFieldUpdater.newUpdater(SourceSubscriber.class, "requested");

    public SourceSubscriber(int maxConcurrency, Subscriber<T> s, CompositeSubscription csub) {
      super(s);
      this.maxConcurrency = maxConcurrency;
      this.s = s;
      this.csub = csub;
      this.guard = new Object();
      this.queue = new ArrayDeque<Observable<? extends T>>(maxConcurrency);
      this.subscribers = Collections.synchronizedList(new ArrayList<MergeItemSubscriber>());
      this.wip = 1;
    }

    @Override
    public void onStart() {
      request(maxConcurrency);
    }

    @Override
    public void onNext(Observable<? extends T> t) {
      synchronized (guard) {
        queue.add(t);
      }
      subscribeNext();
    }

    void subscribeNext() {
      Observable<? extends T> t;
      synchronized (guard) {
        t = queue.peek();
        if (t == null || active >= maxConcurrency) {
          return;
        }
        active++;
        queue.poll();
      }

      MergeItemSubscriber itemSub = new MergeItemSubscriber(SOURCE_INDEX.getAndIncrement(this));
      subscribers.add(itemSub);

      csub.add(itemSub);

      WIP.incrementAndGet(this);

      t.unsafeSubscribe(itemSub);

      request(1);
    }

    @Override
    public void onError(Throwable e) {
      Object[] active;
      synchronized (subscribers) {
        active = subscribers.toArray();
        subscribers.clear();
      }

      try {
        s.onError(e);

        unsubscribe();
      } finally {
        for (Object o : active) {
          @SuppressWarnings("unchecked")
          MergeItemSubscriber a = (MergeItemSubscriber) o;
          a.release();
        }
      }
    }

    @Override
    public void onCompleted() {
      WIP.decrementAndGet(this);
      drain();
    }

    protected void downstreamRequest(long n) {
      for (; ; ) {
        long r = requested;
        long u;
        if (r != Long.MAX_VALUE && n == Long.MAX_VALUE) {
          u = Long.MAX_VALUE;
        } else if (r + n < 0) {
          u = Long.MAX_VALUE;
        } else {
          u = r + n;
        }
        if (REQUESTED.compareAndSet(this, r, u)) {
          break;
        }
      }
      drain();
    }

    protected void drain() {
      synchronized (this) {
        if (emitting) {
          missedEmitting++;
          return;
        }
        emitting = true;
        missedEmitting = 0;
      }
      final List<SourceSubscriber<T>.MergeItemSubscriber> subs = subscribers;
      final Subscriber<T> child = s;
      Object[] active = new Object[subs.size()];
      do {
        long r;

        outer:
        while ((r = requested) > 0) {
          int idx = lastIndex;
          synchronized (subs) {
            if (subs.size() == active.length) {
              active = subs.toArray(active);
            } else {
              active = subs.toArray();
            }
          }

          int resumeIndex = 0;
          int j = 0;
          for (Object o : active) {
            @SuppressWarnings("unchecked")
            MergeItemSubscriber e = (MergeItemSubscriber) o;
            if (e.index == idx) {
              resumeIndex = j;
              break;
            }
            j++;
          }
          int sumConsumed = 0;
          for (int i = 0; i < active.length; i++) {
            j = (i + resumeIndex) % active.length;

            @SuppressWarnings("unchecked")
            final MergeItemSubscriber e = (MergeItemSubscriber) active[j];
            final RxRingBuffer b = e.buffer;
            lastIndex = e.index;

            if (!e.once && b.peek() == null) {
              subs.remove(e);

              synchronized (guard) {
                this.active--;
              }
              csub.remove(e);

              e.release();

              subscribeNext();

              WIP.decrementAndGet(this);

              continue outer;
            }

            int consumed = 0;
            Object v;
            while (r > 0 && (v = b.poll()) != null) {
              nl.accept(child, v);
              if (child.isUnsubscribed()) {
                return;
              }
              r--;
              consumed++;
            }
            if (consumed > 0) {
              sumConsumed += consumed;
              REQUESTED.addAndGet(this, -consumed);
              e.requestMore(consumed);
            }
            if (r == 0) {
              break outer;
            }
          }
          if (sumConsumed == 0) {
            break;
          }
        }

        if (active.length == 0) {
          if (wip == 0) {
            child.onCompleted();
            return;
          }
        }
        synchronized (this) {
          if (missedEmitting == 0) {
            emitting = false;
            break;
          }
          missedEmitting = 0;
        }
      } while (true);
    }

    final class MergeItemSubscriber extends Subscriber<T> {
      volatile boolean once = true;
      final int index;
      final RxRingBuffer buffer;

      public MergeItemSubscriber(int index) {
        buffer = RxRingBuffer.getSpmcInstance();
        this.index = index;
      }

      @Override
      public void onStart() {
        request(RxRingBuffer.SIZE);
      }

      @Override
      public void onNext(T t) {
        try {
          buffer.onNext(t);
        } catch (MissingBackpressureException ex) {
          onError(ex);
          return;
        }

        drain();
      }

      @Override
      public void onError(Throwable e) {
        SourceSubscriber.this.onError(e);
      }

      @Override
      public void onCompleted() {
        if (once) {
          once = false;
          drain();
        }
      }
      /** Request more from upstream. */
      void requestMore(long n) {
        request(n);
      }

      void release() {
        // NO-OP for now
        buffer.release();
      }
    }
  }
예제 #9
0
    protected void drain() {
      synchronized (this) {
        if (emitting) {
          missedEmitting++;
          return;
        }
        emitting = true;
        missedEmitting = 0;
      }
      final List<SourceSubscriber<T>.MergeItemSubscriber> subs = subscribers;
      final Subscriber<T> child = s;
      Object[] active = new Object[subs.size()];
      do {
        long r;

        outer:
        while ((r = requested) > 0) {
          int idx = lastIndex;
          synchronized (subs) {
            if (subs.size() == active.length) {
              active = subs.toArray(active);
            } else {
              active = subs.toArray();
            }
          }

          int resumeIndex = 0;
          int j = 0;
          for (Object o : active) {
            @SuppressWarnings("unchecked")
            MergeItemSubscriber e = (MergeItemSubscriber) o;
            if (e.index == idx) {
              resumeIndex = j;
              break;
            }
            j++;
          }
          int sumConsumed = 0;
          for (int i = 0; i < active.length; i++) {
            j = (i + resumeIndex) % active.length;

            @SuppressWarnings("unchecked")
            final MergeItemSubscriber e = (MergeItemSubscriber) active[j];
            final RxRingBuffer b = e.buffer;
            lastIndex = e.index;

            if (!e.once && b.peek() == null) {
              subs.remove(e);

              synchronized (guard) {
                this.active--;
              }
              csub.remove(e);

              e.release();

              subscribeNext();

              WIP.decrementAndGet(this);

              continue outer;
            }

            int consumed = 0;
            Object v;
            while (r > 0 && (v = b.poll()) != null) {
              nl.accept(child, v);
              if (child.isUnsubscribed()) {
                return;
              }
              r--;
              consumed++;
            }
            if (consumed > 0) {
              sumConsumed += consumed;
              REQUESTED.addAndGet(this, -consumed);
              e.requestMore(consumed);
            }
            if (r == 0) {
              break outer;
            }
          }
          if (sumConsumed == 0) {
            break;
          }
        }

        if (active.length == 0) {
          if (wip == 0) {
            child.onCompleted();
            return;
          }
        }
        synchronized (this) {
          if (missedEmitting == 0) {
            emitting = false;
            break;
          }
          missedEmitting = 0;
        }
      } while (true);
    }