001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.modeshape.common.collection.ring; 018 019import java.util.concurrent.TimeoutException; 020import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; 021import java.util.concurrent.locks.LockSupport; 022import org.modeshape.common.collection.ring.GarbageCollectingConsumer.Collectable; 023import org.modeshape.common.util.CheckArg; 024 025/** 026 * A single-threaded cursor for a ring buffer that ensures it does not pass the slowest {@link Pointer} that is consuming entries. 027 * If the cursor needs to advance but cannot due to a slow {@link Pointer}, then the supplied {@link WaitStrategy strategy} will 028 * be used. As new positions are {@link #publish(long) published}, the supplied {@link WaitStrategy} (used by consumers waiting 029 * for this cursor to advance) will be {@link WaitStrategy#signalAllWhenBlocking() signalled}. 030 * 031 * @author Randall Hauch (rhauch@redhat.com) 032 */ 033public class SingleProducerCursor implements Cursor { 034 035 private static final AtomicReferenceFieldUpdater<SingleProducerCursor, Pointer[]> STAY_BEHIND_UPDATER = AtomicReferenceFieldUpdater.newUpdater(SingleProducerCursor.class, 036 Pointer[].class, 037 "stayBehinds"); 038 039 private final int bufferSize; 040 protected final Pointer current = new Pointer(Pointer.INITIAL_VALUE); 041 protected final WaitStrategy waitStrategy; 042 private long nextPosition = Pointer.INITIAL_VALUE; 043 private long slowestConsumerPosition = Pointer.INITIAL_VALUE; 044 protected volatile long finalPosition = Long.MAX_VALUE; 045 protected volatile Pointer[] stayBehinds = new Pointer[0]; 046 047 public SingleProducerCursor( int bufferSize, 048 WaitStrategy waitStrategy ) { 049 CheckArg.isPositive(bufferSize, "cursor.getBufferSize()"); 050 CheckArg.isPowerOfTwo(bufferSize, "cursor.getBufferSize()"); 051 this.bufferSize = bufferSize; 052 this.waitStrategy = waitStrategy; 053 } 054 055 @Override 056 public long getCurrent() { 057 return nextPosition; 058 } 059 060 @Override 061 public int getBufferSize() { 062 return bufferSize; 063 } 064 065 @Override 066 public long claim() { 067 return claimUpTo(1); 068 } 069 070 @Override 071 public long claim( int number ) { 072 return claimUpTo(number); 073 } 074 075 /** 076 * Claim up to the supplied number of positions. 077 * 078 * @param number the maximum number of positions to claim for writing; must be positive 079 * @return the highest position that were claimed 080 */ 081 protected long claimUpTo( int number ) { 082 assert number > 0; 083 long nextPosition = this.nextPosition; 084 long maxPosition = nextPosition + number; 085 long wrapPoint = maxPosition - bufferSize; 086 long cachedSlowestConsumerPosition = this.slowestConsumerPosition; 087 088 if (wrapPoint > cachedSlowestConsumerPosition || cachedSlowestConsumerPosition > nextPosition) { 089 long minPosition; 090 while (wrapPoint > (minPosition = positionOfSlowestPointer(nextPosition))) { 091 // This takes on the order of tens of nanoseconds, so it's a useful activity to pause a bit. 092 LockSupport.parkNanos(1L); 093 waitStrategy.signalAllWhenBlocking(); 094 } 095 this.slowestConsumerPosition = minPosition; 096 } 097 098 this.nextPosition = maxPosition; 099 return maxPosition; 100 101 } 102 103 protected long positionOfSlowestPointer( long minimumPosition ) { 104 return Pointers.getMinimum(stayBehinds, minimumPosition); 105 } 106 107 protected long positionOfSlowestConsumer() { 108 return slowestConsumerPosition; 109 } 110 111 @Override 112 public boolean publish( long position ) { 113 if (finalPosition != Long.MAX_VALUE) return false; 114 current.set(position); 115 waitStrategy.signalAllWhenBlocking(); 116 return true; 117 } 118 119 @Override 120 public long getHighestPublishedPosition( long lowerPosition, 121 long upperPosition ) { 122 // There is only one producer, so we know that all supplied entries are okay 123 return upperPosition; 124 } 125 126 @Override 127 public PointerBarrier newBarrier() { 128 return new PointerBarrier() { 129 private boolean closed = false; 130 131 @Override 132 public long waitFor( long position ) throws InterruptedException, TimeoutException { 133 if (position > finalPosition) { 134 // The consumer is waiting for a position beyond the final position, meaning we're done ... 135 return -1; 136 } 137 long availableSequence = waitStrategy.waitFor(position, current, current, this); 138 if (availableSequence < position) { 139 return availableSequence; 140 } 141 return getHighestPublishedPosition(position, availableSequence); 142 } 143 144 @Override 145 public boolean isComplete() { 146 return closed || SingleProducerCursor.this.isComplete(); 147 } 148 149 @Override 150 public void close() { 151 this.closed = true; 152 } 153 }; 154 } 155 156 @Override 157 public void signalConsumers() { 158 waitStrategy.signalAllWhenBlocking(); 159 } 160 161 @Override 162 public void complete() { 163 finalPosition = current.get(); 164 waitStrategy.signalAllWhenBlocking(); 165 } 166 167 @Override 168 public boolean isComplete() { 169 return finalPosition == current.get(); 170 } 171 172 @Override 173 public Pointer newPointer() { 174 Pointer result = new Pointer(current.get()); 175 this.stayBehind(result); 176 return result; 177 } 178 179 @Override 180 public void stayBehind( Pointer... pointers ) { 181 Pointers.add(this, STAY_BEHIND_UPDATER, this, pointers); 182 } 183 184 @Override 185 public boolean ignore( Pointer pointer ) { 186 return Pointers.remove(this, STAY_BEHIND_UPDATER, pointer); 187 } 188 189 @Override 190 public GarbageCollectingConsumer createGarbageCollectingConsumer( Collectable collectable ) { 191 return new GarbageCollectingConsumer(this, current, waitStrategy, collectable); 192 } 193}