final void writeToQueue(final E[] buffer, final E e, final long index, final long offset) { soElement(buffer, offset, e);// StoreStore soProducerIndex(index + 1);// this ensures atomic write of long on 32bit platforms }
protected final void soNext(E[] curr, E[] next) { long offset = nextArrayOffset(curr); soElement(curr, offset, next); }
final void linkOldToNew( final long currIndex, final E[] oldBuffer, final long offset, final E[] newBuffer, final long offsetInNew, final E e) { soElement(newBuffer, offsetInNew, e);// StoreStore // link to next buffer and add next indicator as element of old buffer soNext(oldBuffer, newBuffer); soElement(oldBuffer, offset, JUMP); // index is visible after elements (isEmpty/poll ordering) soProducerIndex(currIndex + 1);// this ensures atomic write of long on 32bit platforms }
private boolean addSlowPath(E[] buffer, long mask, E newVal, int hash) { final int limit = (int) (hash + mask); for (int i = hash + 1; i <= limit; i++) { final long offset = calcElementOffset(i, mask); final E currVal = lpElement(buffer, offset); if (currVal == null) { size++; soElement(buffer, offset, newVal); return true; } else if (newVal.equals(currVal)) { return false; } } return false; }
private E removeElement(final E[] buffer, long index, final long mask) { final long offset = calcElementOffset(index, mask); // load plain, element happens before it's index becomes visible final E e = lpElement(buffer, offset); // store ordered, make sure nulling out is visible. Producer is waiting for this value. soElement(buffer, offset, null); return e; }
@SuppressWarnings("unchecked") protected final E[] lvNextArrayAndUnlink(E[] curr) { final long offset = nextArrayOffset(curr); final E[] nextBuffer = (E[]) lvElement(curr, offset); // prevent GC nepotism soElement(curr, offset, null); return nextBuffer; }
@SuppressWarnings("unchecked") private E[] getNextBuffer(final E[] buffer, final long mask) { final long offset = nextArrayOffset(mask); final E[] nextBuffer = (E[]) lvElement(buffer, offset); soElement(buffer, offset, null); return nextBuffer; }
private void soCycleElement(E[] buffer, E e, int activeCycleIndex, int positionWithinCycle, int cycleLengthLog2) { final int indexInBuffer = calcElementIndexInBuffer(positionWithinCycle, activeCycleIndex, cycleLengthLog2); final long offset = UnsafeRefArrayAccess.calcElementOffset(indexInBuffer); soElement(buffer, offset, e); }
@Override public boolean offer(final E e) { if (null == e) { throw new NullPointerException("Null is not a valid element"); } final E[] lb = buffer; final long t = pIndex; final long offset = calcElementOffset(t); if (null != lvElement(lb, offset)) { // read acquire return false; } soElement(lb, offset, e); // write release pIndex = t + 1; return true; }
@Override public E poll() { long cIndex = this.cIndex; final long offset = calcElementOffset(cIndex); final E[] lb = buffer; final E e = lvElement(lb, offset); // write acquire if (null == e) { return null; } soElement(lb, offset, null); // read release this.cIndex = cIndex + 1; return e; }
private E newBufferPoll(E[] nextBuffer, long index) { final long offset = newBufferAndOffset(nextBuffer, index); final E n = lvElement(nextBuffer, offset);// LoadLoad if (n == null) { throw new IllegalStateException("new buffer must have at least one element"); } soElement(nextBuffer, offset, null);// StoreStore soConsumerIndex(index + 2); return n; }
private void addForResize(final E[] buffer, final long mask, E newVal) { final int hash = rehash(newVal.hashCode()); final int limit = (int) (hash + mask); for (int i = hash; i <= limit; i++) { final long offset = calcElementOffset(i, mask); final E currVal = lpElement(buffer, offset); if (currVal == null) { soElement(buffer, offset, newVal); return; } } }
private boolean offerAndWakeup(E[] buffer, long mask, long pIndex, E e) { final long offset = modifiedCalcElementOffset(pIndex, mask); final Thread consumerThread = lvBlocked(); // We could see a null here through a race with the consumer not yet storing the reference, or through a race // with another producer. Just retry. if (consumerThread == null) { return false; } // Claim the slot and the responsibility of unparking if(!casProducerIndex(pIndex, pIndex + 1)) { return false; } soElement(buffer, offset, e); soBlocked(null); // releases the consumer from the park loop LockSupport.unpark(consumerThread); return true; }
@Override public boolean add(E newVal) { final E[] buffer = this.buffer; final long mask = buffer.length - 1; final int hash = rehash(newVal.hashCode()); final long offset = calcElementOffset(hash, mask); final E currVal = lpElement(buffer, offset); boolean result; if (currVal == null) { size++; soElement(buffer, offset, newVal); result = true; } else { result = !newVal.equals(currVal) && addSlowPath(buffer, mask, newVal, hash); } if (result && size > resizeThreshold) { resize(); } return result; }
private E newBufferPoll(final E[] buffer, final long index) { E[] nextBuffer = lvNextArrayAndUnlink(buffer); consumerBuffer = nextBuffer; final long mask = length(nextBuffer) - 2; consumerMask = mask; final long offset = calcElementOffset(index, mask); final E n = lvElement(nextBuffer, offset);// LoadLoad if (null == n) { throw new IllegalStateException("new buffer must have at least one element"); } else { soConsumerIndex(index + 1);// this ensures correctness on 32bit platforms soElement(nextBuffer, offset, null);// StoreStore return n; } } }
@SuppressWarnings("unchecked") @Override public E relaxedPoll() { final E[] buffer = consumerBuffer; final long index = lpConsumerIndex(); final long mask = consumerMask; final long offset = modifiedCalcElementOffset(index, mask); E e = lvElement(buffer, offset);// LoadLoad if (e == null) { return null; } soElement(buffer, offset, null); soConsumerIndex(index + 2); return e; }
private boolean removeSlowPath(Object val, E[] buffer, long mask, int hash) { final int limit = (int) (hash + mask); for (int searchIndex = hash + 1; searchIndex <= limit; searchIndex++) { final long offset = calcElementOffset(searchIndex, mask); final E e = lpElement(buffer, offset); if (e == null) { return false; } else if (val.equals(e)) { size--; if (lpElement(buffer, calcElementOffset(searchIndex + 1, mask)) == null) { soElement(buffer, offset, null); } else { compactAndRemove(buffer, mask, searchIndex); } return true; } } return false; }
/** * {@inheritDoc} * <p> * This implementation is correct for single consumer thread use only. */ @Override public E poll() { final long consumerIndex = this.lpConsumerIndex(); final long offset = calcElementOffset(consumerIndex); // local load of field to avoid repeated loads after volatile reads final E[] buffer = this.buffer; final E e = lvElement(buffer, offset);// LoadLoad if (null == e) { return null; } soElement(buffer, offset, null);// StoreStore soConsumerIndex(consumerIndex + 1); // ordered store -> atomic and ordered for size() return e; }
@Override public int fill(final Supplier<E> s, final int limit) { final E[] buffer = this.buffer; final long mask = this.mask; long producerIndex = this.lpProducerIndex(); for (int i = 0; i < limit; i++) { final long offset = calcElementOffset(producerIndex, mask); if (null != lvElement(buffer, offset)) { return i; } producerIndex++; soElement(buffer, offset, s.get()); // StoreStore soProducerIndex(producerIndex); // ordered store -> atomic and ordered for size() } return limit; }
@Override public int drain(final Consumer<E> c, final int limit) { final E[] buffer = this.buffer; final long mask = this.mask; final long consumerIndex = this.lpConsumerIndex(); for (int i = 0; i < limit; i++) { final long index = consumerIndex + i; final long offset = calcElementOffset(index, mask); final E e = lvElement(buffer, offset);// LoadLoad if (null == e) { return i; } soElement(buffer, offset, null);// StoreStore soConsumerIndex(index + 1); // ordered store -> atomic and ordered for size() c.accept(e); } return limit; }