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}