/* 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.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.locks.ReentrantReadWriteLock; import innovimax.quixproc.datamodel.IQuiXStream; import innovimax.quixproc.datamodel.QuiXCharStream; import innovimax.quixproc.datamodel.QuiXException; import innovimax.quixproc.datamodel.event.AQuiXEvent; /** * Simple implementation of {@link Queue} interface It uses a simple * {@link ArrayList} and {@link ReentrantReadWriteLock} to do the job It's far * from efficient * * @author innovimax */ public class SimpleAppendQuiXQueue<T> implements IQuiXQueue<T> { private static final boolean DEBUG = false; final List<T> events; final ReentrantReadWriteLock rwl; // int readerCount = 0; boolean closed = false; private boolean startWorking = false; // private static int counter = 0; private final int rank; private int maxReader; public SimpleAppendQuiXQueue() { this.events = new ArrayList<T>(); this.rwl = new ReentrantReadWriteLock(true); counter++; this.rank = counter; if (DEBUG) System.out.println("CreateSimpleQEQ : " + this.rank); // Thread.dumpStack(); } /* * (non-Javadoc) * * @see * com.xmlcalabash.stream.util.shared.IQuixEventQueue#add(com.xmlcalabash. * stream.util.QuixEvent) */ @Override public void append(T event) { this.startWorking = true; this.rwl.writeLock().lock(); try { if (this.closed) throw new RuntimeException("Cannot append to a closed stream"); this.events.add(event); } finally { this.rwl.writeLock().unlock(); } } /* * (non-Javadoc) * * @see com.xmlcalabash.stream.util.shared.IQuixEventQueue#close() */ @Override public void close() { this.rwl.writeLock().lock(); try { if (this.closed) throw new RuntimeException("Already closed"); this.closed = true; } finally { this.rwl.writeLock().unlock(); } if (DEBUG) System.out.println("CreateSimpleQEQ (closed) : " + this.rank); } private final class LocalReader implements IQuiXStream<T> { // private final Iterator<QuixEvent> iterator; private int i = 0; private boolean readerClosed = false; LocalReader() { // this.iterator = events.iterator(); } @Override public boolean hasNext() { if (this.readerClosed) throw new RuntimeException("Reader already closed"); SimpleAppendQuiXQueue.this.rwl.readLock().lock(); try { if (this.i < SimpleAppendQuiXQueue.this.events.size()) return true; // if (iterator.hasNext()) return true; // si c'est faux ca d�pends de close while (this.i >= SimpleAppendQuiXQueue.this.events .size()/* !iterator.hasNext() */) { if (SimpleAppendQuiXQueue.this.closed) return false; SimpleAppendQuiXQueue.this.rwl.readLock().unlock(); // System.out.println("hasNextBeforeYield"); Thread.yield(); // System.out.println("hasNextAfterYield"); SimpleAppendQuiXQueue.this.rwl.readLock().lock(); } // il n'y a pas de concurrence sur la lecture, chacun lit � son // rythme // donc si il y a un element il l'est toujours return true; } finally { SimpleAppendQuiXQueue.this.rwl.readLock().unlock(); } } @Override public T next() { if (this.readerClosed) throw new RuntimeException("Reader already closed"); SimpleAppendQuiXQueue.this.rwl.readLock().lock(); try { if (this.i < SimpleAppendQuiXQueue.this.events.size()) { return SimpleAppendQuiXQueue.this.events.get(this.i++); } // if (iterator.hasNext()) return iterator.next(); // si c'est faux ca d�pends de close while (this.i >= SimpleAppendQuiXQueue.this.events .size()/* !iterator.hasNext() */) { if (SimpleAppendQuiXQueue.this.closed) return null; SimpleAppendQuiXQueue.this.rwl.readLock().unlock(); // System.out.println("nextBeforeYield"); Thread.yield(); // System.out.println("nextAfterYield"); SimpleAppendQuiXQueue.this.rwl.readLock().lock(); } // il n'y a pas de concurrence sur la lecture, chacun lit � son // rythme // donc si il y a un element il l'est toujours return SimpleAppendQuiXQueue.this.events.get(this.i++); // return iterator.next(); } finally { SimpleAppendQuiXQueue.this.rwl.readLock().unlock(); } } @Override public void close() { if (DEBUG) System.out.println("CreateSimpleQEQ (close reader " + SimpleAppendQuiXQueue.this.readerCount + ") : " + SimpleAppendQuiXQueue.this.rank); if (!this.readerClosed) { this.readerClosed = true; SimpleAppendQuiXQueue.this.readerCount--; if (DEBUG) System.out.println("CreateSimpleQEQ (really close reader " + SimpleAppendQuiXQueue.this.readerCount + ") : " + SimpleAppendQuiXQueue.this.rank); if (SimpleAppendQuiXQueue.this.readerCount == 0) { SimpleAppendQuiXQueue.this.events.clear(); if (DEBUG) System.out.println("CreateSimpleQEQ (CLEAR) : " + SimpleAppendQuiXQueue.this.rank); } } } } // @Override /* * (non-Javadoc) * * @see com.xmlcalabash.stream.util.shared.IQuixEventQueue#registerReader() */ @Override public IQuiXStream<T> registerReader() { // if (startWorking) throw new RuntimeException("Cannot register reader // after the queue already been fed"); if (DEBUG) System.out.println("CreateSimpleQEQ (open reader " + this.readerCount + ") : " + this.rank); this.readerCount++; return new LocalReader(); } // register proxy reader is there for for-each loop where you never know the // true number of reader // the idea is that a future version of this object should be able to drop // the data // that would never be reused by being sure that each of the registered // reader // has read it /* * (non-Javadoc) * * @see * com.xmlcalabash.stream.util.shared.IQuixEventQueue#registerProxyReader() */ @Override public ProxyReader<T> registerProxyReader() { return null; } /* * (non-Javadoc) * * @see com.xmlcalabash.stream.util.shared.IQuixEventQueue#setReaderCount() */ @Override public void setReaderCount(int count) { this.readerCount = count; } // public synchronized void clear() { // synchronized(events) { // events.clear(); // } // } // private void clean() { // if (readerCount == 0) { // events.clear(); // } // } @Override public void closeReaderRegistration() { this.maxReader = this.readerCount; } static final int MAX_PRODUCE = 10000000; static final int LOG_MODULO = MAX_PRODUCE / 10; private static class SimpleProducer implements Runnable { private final IQuiXQueue<AQuiXEvent> qeq; SimpleProducer(IQuiXQueue<AQuiXEvent> qeq) { this.qeq = qeq; } @Override public void run() { int i = MAX_PRODUCE; while (i-- > 0) { // try { this.qeq.append(AQuiXEvent.getStartDocument(QuiXCharStream.fromSequence(String.valueOf(i)))); if (i % LOG_MODULO == 0) System.out.println("Produce " + i); // Thread.sleep(1); // } catch (InterruptedException e) { // TODO Auto-generated catch block // e.printStackTrace(); // } } this.qeq.close(); } } private static class SimpleConsumer implements Runnable { private final IQuiXStream<AQuiXEvent> qs; private final int rank; SimpleConsumer(IQuiXStream<AQuiXEvent> qs, int rank) { this.qs = qs; this.rank = rank; } @Override public void run() { try { int i = 0; while (this.qs.hasNext()) { this.qs.next(); i++; if (i % LOG_MODULO == 0) System.out.println("Consume " + this.rank); } } catch (QuiXException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.qs.close(); } } public static void main(String[] args) { System.out.println("Start"); System.out.println("Create QuixEventQueue"); // IQueue<QuixEvent> qeq = new SimpleAppendQueue<QuixEvent>(); SmartAppendQuiXQueue<AQuiXEvent> qeq = new SmartAppendQuiXQueue<AQuiXEvent>(); final int READER_COUNT = 20; qeq.setReaderCount(READER_COUNT); System.out.println("Create SimpleProducer"); SimpleProducer sp = new SimpleProducer(qeq); for (int i = 0; i < READER_COUNT; i++) { System.out.println("Create SimpleConsumer"); SimpleConsumer sc = new SimpleConsumer(qeq.registerReader(), i); Thread t = new Thread(sc); System.out.println("Start SimpleConsumer"); t.start(); } Thread t = new Thread(sp); System.out.println("Start SimpleProducer"); t.start(); } }