/* QuiXProc: efficient evaluation of XProc Pipelines. Copyright (C) 2011-2015 Innovimax All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package innovimax.quixproc.datamodel.shared; import java.util.Collections; import java.util.Set; import java.util.TreeSet; //import java.util.concurrent.locks.AbstractQueuedSynchronizer; import innovimax.quixproc.datamodel.IQuiXStream; public final class SmartAppendQuiXQueue<T> implements IQuiXQueue<T> { private static final int DEBUG_LEVEL = 0; // 0 none, 1 simple, 2 detailled private static int counter = 0; private static Set<Integer> open = Collections.synchronizedSet(new TreeSet<Integer>()); private LinkedItem<T> head; private LinkedItem<T> current; private int readerCount, currentReader, rank; /** * Item of manul LinkedList * * @author innovimax */ private static class LinkedItem<T> { // private static class BooleanLatch { // // private static class Sync extends AbstractQueuedSynchronizer { // boolean isSignalled() { // return getState() != 0; // } // // protected int tryAcquireShared(int ignore) { // return isSignalled() ? 1 : -1; // } // // protected boolean tryReleaseShared(int ignore) { // setState(1); // return true; // } // } // // private final Sync sync = new Sync(); // // // public boolean isSignalled() { return sync.isSignalled(); } // public void signal() { // sync.releaseShared(1); // } // // public void await() throws InterruptedException { // sync.acquireSharedInterruptibly(1); // } // } public static final LinkedItem END = null; private final T event; // private final BooleanLatch latch; private Object lock; // private LinkedItem<T> next; public LinkedItem(T event) { this.event = event; // this.latch = new BooleanLatch(); this.lock = new Object(); } public T get() { return this.event; } // see to get around locking http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html public LinkedItem<T> getNext() { try { // this.latch.await(); if (this.lock != null) { if (this.lock != null) { if (this.lock != null) { if (this.lock != null) { synchronized (this.lock) { if (this.lock != null) this.lock.wait(); } } } } } return this.next; } catch (InterruptedException e) { e.printStackTrace(); return null; } } public void setNext(LinkedItem<T> li) { this.next = li; // this.latch.signal(); synchronized (this.lock) { this.lock.notifyAll(); this.lock = null; } } } private static final class LocalReader<T> implements IQuiXStream<T> { private LinkedItem<T> current; // debug private String name; LocalReader(LinkedItem<T> li) { this.current = li; } private void setName(String name) { this.name = name; } @Override public boolean hasNext() { if (this.current == null) { System.out.println("hasNext => current == null in LocalReader"); return false; } boolean result = this.current.getNext() != LinkedItem.END; if (DEBUG_LEVEL > 1) if (!result) System.out.println("Reader(" + this.name + ") hasnext=false"); return result; } @Override public T next() { this.current = this.current.getNext(); T event = this.current.get(); if (DEBUG_LEVEL > 1) System.out.println(counter + "/" + this.name + "<-" + event); return event; } @Override public void close() { this.current = LinkedItem.END; if (DEBUG_LEVEL > 0) System.out.println("Reader(" + this.name + ") closed"); } } public SmartAppendQuiXQueue() { this.head = new LinkedItem<T>(null); this.current = this.head; this.currentReader = 0; this.readerCount = 0; this.rank = counter++; if (DEBUG_LEVEL > 0) System.out.println("SmartAppendQueue Create " + this.rank); if (DEBUG_LEVEL > 0) open.add(this.rank); } /** * !!! NOT THREAD SAFE : Only one thread should do the appending here */ @Override public void append(T event) { if (DEBUG_LEVEL > 1) System.out.println(counter + "->" + event); LinkedItem<T> li = new LinkedItem<T>(event); this.current.setNext(li); this.current = li; } /** * !!! NOT THREAD SAFE : Only one thread should do the closing here */ @Override public void close() { this.current.setNext(LinkedItem.END); this.current = LinkedItem.END; if (DEBUG_LEVEL > 0) open.remove(this.rank); if (DEBUG_LEVEL > 0) System.out.println("SmartAppendQueue Close : " + this.rank + "; SmartAppend still open : " + open.size() + "; Reader(" + this.currentReader + "/" + this.readerCount + ")"); } @Override public IQuiXStream<T> registerReader() { final LinkedItem<T> local_head = this.head; if (DEBUG_LEVEL > 0) System.out.println("head " + this.head); LocalReader<T> l = new LocalReader<T>(local_head); IQuiXStream<T> result = l; if (DEBUG_LEVEL > 0) l.setName(this.rank + "/" + this.currentReader + "/" + this.readerCount); this.currentReader++; if (this.readerCount > this.currentReader) { // do nothing there is still reader to register // closeReaderRegistration(); } else if (this.readerCount == this.currentReader) { // we reach the maximum so clear head closeReaderRegistration(); } else { closeReaderRegistration(); // readerCount < currentReader throw new RuntimeException( // System.out.println( "readerCount < currentReader : " + this.readerCount + "," + this.currentReader); } return result; } private static final class LocalProxyReader<T> implements ProxyReader<T> { private LinkedItem<T> head; LocalProxyReader(LinkedItem<T> head) { this.head = head; } @Override public IQuiXStream<T> registerReader() { return new LocalReader<T>(this.head); } @Override public void closeReaderRegistration() { this.head = LinkedItem.END; } } @Override public ProxyReader<T> registerProxyReader() { // TODO Auto-generated method stub return new LocalProxyReader<T>(this.head); } @Override public void setReaderCount(int count) { if (count < 1) count = 1; this.readerCount = count; if (DEBUG_LEVEL > 0) if (count >= 18) Thread.dumpStack(); if (DEBUG_LEVEL > 0) System.out.println("SetReaderCount (" + this.rank + ") = " + count); } @Override public void closeReaderRegistration() { this.head = LinkedItem.END; if (DEBUG_LEVEL > 0) System.out.println("closeReaderRegistration()"); } }