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.CountDownLatch;
020import java.util.concurrent.TimeoutException;
021import java.util.concurrent.atomic.AtomicBoolean;
022
023/**
024 * @author Randall Hauch (rhauch@redhat.com)
025 */
026public class GarbageCollectingConsumer implements Runnable, AutoCloseable, DependentOnPointers {
027
028    public static interface Collectable {
029        void collect( long position );
030    }
031
032    private final Cursor cursor;
033    private final PointerBarrier gcBarrier;
034    private final Pointer pointer;
035    protected final TrailingPointer trailingPointer;
036    private final AtomicBoolean runThread = new AtomicBoolean(true);
037    private final CountDownLatch stopLatch = new CountDownLatch(1);
038    private final Collectable collectable;
039
040    public GarbageCollectingConsumer( final Cursor cursor,
041                                      Pointer cursorPointer,
042                                      final WaitStrategy waitStrategy,
043                                      Collectable collectable ) {
044        this.cursor = cursor;
045        this.pointer = new Pointer();
046        this.trailingPointer = new TrailingPointer(cursorPointer);
047        this.cursor.stayBehind(this.pointer);
048        this.collectable = collectable;
049        // Create the pointer barrier that will be called only when used ...
050        this.gcBarrier = new PointerBarrier() {
051            private volatile boolean closed = false;
052
053            @Override
054            public long waitFor( long position ) throws InterruptedException, TimeoutException {
055                if (cursor.isComplete()) {
056                    // The cursor is done, so return -1 ...
057                    return -1;
058                }
059
060                // Use the gcPointer instead ...
061                long availableSequence = waitStrategy.waitFor(position, trailingPointer, trailingPointer, this);
062                if (availableSequence < position) {
063                    return availableSequence;
064                }
065                return cursor.getHighestPublishedPosition(position, availableSequence);
066            }
067
068            @Override
069            public boolean isComplete() {
070                return closed || cursor.isComplete();
071            }
072
073            @Override
074            public void close() {
075                this.closed = true;
076            }
077        };
078    }
079
080    @Override
081    public void run() {
082        try {
083            while (runThread.get()) {
084                long next = pointer.get() + 1L;
085                try {
086                    // Try to find the next position we can read to ...
087                    long maxPosition = gcBarrier.waitFor(next);
088                    while (next <= maxPosition) {
089                        collectable.collect(next);
090                        if (!runThread.get()) return;
091                        next = pointer.incrementAndGet() + 1L;
092                    }
093                    if (maxPosition < 0) {
094                        // The buffer has been shutdown and there are no more positions, so we're done ...
095                        return;
096                    }
097                } catch (TimeoutException e) {
098                    // It took too long to wait, but just continue ...
099                } catch (InterruptedException e) {
100                    // The thread was interrupted ...
101                    Thread.interrupted();
102                    break;
103                } catch (RuntimeException e) {
104                    // Don't retry this entry, so just advance the pointer and continue ...
105                    pointer.incrementAndGet();
106                }
107            }
108        } finally {
109            // When we're done, so tell the cursor to ignore our pointer ...
110            try {
111                cursor.ignore(pointer);
112            } finally {
113                stopLatch.countDown();
114            }
115        }
116    }
117
118    @Override
119    public void close() {
120        if (this.runThread.compareAndSet(true, false)) {
121            try {
122                this.stopLatch.await();
123            } catch (InterruptedException e) {
124                // do nothing ...
125            }
126        }
127    }
128
129    @Override
130    public boolean ignore( Pointer pointer ) {
131        return trailingPointer.ignore(pointer);
132    }
133
134    @Override
135    public void stayBehind( Pointer... pointers ) {
136        trailingPointer.stayBehind(pointers);
137    }
138
139}