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}