package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.TimeoutException; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,sequential=false) public class QueueTest { public static void testQueue() throws QueueClosedException { final Queue queue=new Queue(); queue.add("Q1"); queue.add("Q2"); queue.add("Q3"); assert queue.peek().equals("Q1"); assert queue.remove().equals("Q1"); assert queue.peek().equals("Q2"); assert queue.remove().equals("Q2"); queue.add("Q5"); queue.close(true); try { queue.add("Q6"); assert false : "should not get here"; } catch(org.jgroups.util.QueueClosedException qc) { } int size=queue.size(); queue.removeElement("Q5"); assert queue.size() == size -1; assert queue.peek().equals("Q3"); assert queue.remove().equals("Q3"); assert queue.closed(); } @Test(expectedExceptions=QueueClosedException.class) public static void testCloseWithoutFlush() throws QueueClosedException { final Queue queue=new Queue(); queue.close(false); queue.remove(); } @Test(expectedExceptions=QueueClosedException.class) public static void testCloseWithFlush() throws QueueClosedException { final Queue queue=new Queue(); queue.close(true); queue.remove(); } @Test(expectedExceptions=QueueClosedException.class) public static void testCloseWithFlush2() throws QueueClosedException { final Queue queue=new Queue(); queue.add(new Integer(1)); queue.add(new Integer(2)); queue.add(new Integer(3)); queue.close(true); for(int i=1; i <= 3; i++) { Object obj=queue.remove(); assert obj != null; assert new Integer(i).equals(obj); } queue.remove(); } public static void testValues() throws QueueClosedException { final Queue queue=new Queue(); queue.add(new Integer(1)); queue.add(new Integer(3)); queue.add(new Integer(99)); queue.add(new Integer(8)); System.out.println("queue: " + Util.dumpQueue(queue)); int size=queue.size(); assert size == 4; LinkedList values=queue.values(); assert values.size() == size; } public static void testLargeInsertion() throws QueueClosedException { String element="MyElement"; long start, stop; final Queue queue=new Queue(); System.out.println("Inserting 100000 elements"); start=System.currentTimeMillis(); for(int i=0; i < 100000; i++) queue.add(element); stop=System.currentTimeMillis(); System.out.println("Took " + (stop - start) + " msecs"); System.out.println("Removing 100000 elements"); start=System.currentTimeMillis(); while(queue.size() > 0) queue.remove(); stop=System.currentTimeMillis(); System.out.println("Took " + (stop - start) + " msecs"); } public static void testEmptyQueue() { final Queue queue=new Queue(); assert queue.getFirst() == null; assert queue.getLast() == null; } public static void testAddAll() throws QueueClosedException { final Queue queue=new Queue(); List<String> l=new ArrayList<String>(); l.add("one"); l.add("two"); l.add("three"); queue.addAll(l); System.out.println("queue is " + queue); assert queue.size() == 3; assert queue.remove().equals("one"); assert queue.size() == 2; assert queue.remove().equals("two"); assert queue.size() == 1; assert queue.remove().equals("three"); assert queue.size() == 0; } public static void testInsertionAndRemoval() throws Exception { final Queue queue=new Queue(); String s1="Q1", s2="Q2"; queue.add(s1); assert queue.getFirst() != null; assert queue.getLast() != null; assert queue.getLast().equals(queue.getFirst()); queue.add(s2); assert queue.getFirst() != queue.getLast(); Object o1=queue.peek(); Object o2=queue.getFirst(); System.out.println("o1=" + o1 + ", o2=" + o2 + ", o1.equals(o2)=" + o1.equals(o2)); assert queue.getFirst().equals(queue.peek()); queue.remove(); assert queue.size() == 1; assert queue.getLast().equals(queue.getFirst()); queue.remove(); assert queue.size() == 0; assert queue.getFirst() == null; assert queue.getLast() == null; } public static void testWaitUntilClosed() { final Queue queue=new Queue(); queue.close(true); queue.waitUntilClosed(0); assert queue.size() == 0; } public static void testWaitUntilClosed2() { final Queue queue=new Queue(); queue.close(true); try { queue.peek(); assert false : "peek() should throw a QueueClosedException"; } catch(QueueClosedException e) { assert e != null; } assert queue.size() == 0; } public static void testWaitUntilClosed3() throws QueueClosedException { final Queue queue=new Queue(); queue.add("one"); queue.close(true); Object obj=queue.peek(); assert obj.equals("one"); assert queue.size() == 1; queue.remove(); try { queue.peek(); assert false : "peek() should throw a QueueClosedException"; } catch(QueueClosedException e) { assert e != null; } assert queue.size() == 0; } public static void testWaitUntilClosed4() throws QueueClosedException { final Queue queue=new Queue(); for(int i=0; i < 10; i++) queue.add(new Integer(i)); new Thread() { public void run() { while(!queue.closed()) { try { System.out.println("-- removed " + queue.remove()); Util.sleep(200); } catch(QueueClosedException e) { break; } } } }.start(); queue.close(true); queue.waitUntilClosed(0); assert queue.size() == 0; } public static void testWaitUntilClosed5() throws QueueClosedException { final Queue queue=new Queue(); for(int i=0; i < 10; i++) queue.add(new Integer(i)); new Thread() { public void run() { while(!queue.closed()) { try { System.out.println("-- removed " + queue.remove()); Util.sleep(200); } catch(QueueClosedException e) { System.out.println("-- queue is closed, cannot remove element"); break; } } } }.start(); Util.sleep(600); queue.close(false); queue.waitUntilClosed(0); assert queue.size() > 0; } public static void testRemoveElementNoElement() { final Queue queue=new Queue(); String s1="Q1"; try { queue.removeElement(s1); assert !(queue.closed()); assert queue.size() == 0; } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementOneElement() { final Queue queue=new Queue(); String s1="Q1"; try { queue.add(s1); queue.removeElement(s1); assert queue.size() == 0; assert queue.getFirst() == null; assert queue.getLast() == null; } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementTwoElementsFirstFound() { String s1="Q1", s2="Q2"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.removeElement(s1); assert queue.size() == 1; assert queue.getFirst().equals(s2); assert queue.getLast().equals(s2); assert queue.getFirst().equals(queue.getLast()); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementTwoElementsSecondFound() { String s1="Q1", s2="Q2"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.removeElement(s2); assert queue.size() == 1; assert queue.getFirst().equals(s1); assert queue.getLast().equals(s1); assert queue.getFirst().equals(queue.getLast()); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementThreeElementsFirstFound() { String s1="Q1", s2="Q2", s3="Q3"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s1); assert queue.size() == 2; assert queue.getFirst().equals(s2); assert queue.getLast().equals(s3); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementThreeElementsSecondFound() { String s1="Q1", s2="Q2", s3="Q3"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s2); assert queue.size() == 2; assert queue.getFirst().equals(s1); assert queue.getLast().equals(s3); } catch(QueueClosedException ex) { assert false : ex.toString(); } } public static void testRemoveElementThreeElementsThirdFound() { String s1="Q1", s2="Q2", s3="Q3"; final Queue queue=new Queue(); try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s3); assert queue.size() == 2; assert queue.getFirst().equals(s1); assert queue.getLast().equals(s2); } catch(QueueClosedException ex) { assert false : ex.toString(); } } @Test(expectedExceptions=QueueClosedException.class) public static void testRemoveAndClose() throws QueueClosedException { final Queue queue=new Queue(); new Thread() { public void run() { Util.sleep(1000); queue.close(true); // close gracefully } }.start(); queue.remove(); } @Test(expectedExceptions=QueueClosedException.class) public static void testRemoveAndCloseWithTimeout() throws QueueClosedException, TimeoutException { final Queue queue=new Queue(); new Thread() { public void run() { Util.sleep(1000); queue.close(true); // close gracefully } }.start(); queue.remove(5000); } @Test(expectedExceptions=TimeoutException.class) public static void testInterruptAndRemove() throws QueueClosedException, TimeoutException { final Queue queue=new Queue(); Thread.currentThread().interrupt(); queue.remove(2000); } @Test(expectedExceptions=QueueClosedException.class) public static void testRemoveAndInterrupt() throws QueueClosedException { final Queue queue=new Queue(); Thread closer=new Thread() { public void run() { Util.sleep(1000); System.out.println("-- closing queue"); queue.close(false); } }; closer.start(); System.out.println("-- removing element"); queue.remove(); } public static void testClear() throws QueueClosedException { Queue queue=new Queue(); queue.add("one"); queue.add("two"); assert queue.size() == 2; queue.close(true); assert queue.size() == 2; queue.clear(); assert queue.size() == 0; queue=new Queue(); queue.add("one"); queue.add("two"); queue.clear(); assert queue.size() == 0; queue.add("one"); queue.add("two"); assert queue.size() == 2; queue.clear(); assert queue.size() == 0; } // public void testWaitUntilEmpty() { // try { // queue.add("one"); // queue.add("two"); // queue.add("three"); // // new Thread() { // public void run() { // try { // sleep(1000); // queue.remove(); // queue.remove(); // queue.remove(); // } // catch(Exception e) { // } // } // }.start(); // // queue.waitUntilEmpty(0); // assertEquals(queue.size(), 0); // } // catch(Exception e) { // e.printStackTrace(); // fail(e.toString()); // } // } // // public void testWaitUntilEmpty2() { // try { // queue.add("one"); // queue.add("two"); // queue.add("three"); // // new Thread() { // public void run() { // try { // sleep(1000); // queue.remove(); // queue.remove(); // } // catch(Exception e) { // } // } // }.start(); // // queue.waitUntilEmpty(3000); // fail("shouldn't get here; we should have caught a TimeoutException"); // } // catch(TimeoutException timeout) { // assertTrue(true); // } // catch(Exception e) { // e.printStackTrace(); // fail(e.toString()); // } // } // // // public void testWaitUntilQueueClosed() { // try { // queue.add("one"); // queue.add("two"); // queue.add("three"); // // new Thread() { // public void run() { // try { // sleep(1000); // queue.close(false); // } // catch(Exception e) { // } // } // }.start(); // // queue.waitUntilEmpty(0); // fail("shouldn't get here; we should have caught a QueueClosedException"); // } // catch(TimeoutException timeout) { // fail("we should not have gottem here"); // } // catch(QueueClosedException ex2) { // assertTrue(true); // } // catch(Exception e) { // e.printStackTrace(); // fail(); // } // } /** Multiple threads call remove(), one threads then adds an element. Only 1 thread should actually terminate * (the one that has the element) */ public static void testBarrier() throws QueueClosedException { RemoveOneItem[] removers=new RemoveOneItem[10]; final Queue queue=new Queue(); int num_dead=0; for(int i=0; i < removers.length; i++) { removers[i]=new RemoveOneItem(i, queue); removers[i].start(); } Util.sleep(200); System.out.println("-- adding element 99"); queue.add(new Long(99)); System.out.println("-- adding element 100"); queue.add(new Long(100)); long target_time=System.currentTimeMillis() + 10000L; do { int num=0; for(int i=0; i < removers.length; i++) { if(!removers[i].isAlive()) num++; } if(num == 2) break; Util.sleep(500); } while(target_time > System.currentTimeMillis()); for(int i=0; i < removers.length; i++) { System.out.println("remover #" + i + " is " + (removers[i].isAlive() ? "alive" : "terminated")); if(!removers[i].isAlive()) { num_dead++; } } assert num_dead == 2 : "num_dead was " + num_dead + ", but expected 2"; queue.close(false); } /** Multiple threads call remove(), one threads then adds an element. Only 1 thread should actually terminate * (the one that has the element) */ public static void testBarrierWithTimeOut() throws QueueClosedException { final Queue queue=new Queue(); RemoveOneItemWithTimeout[] removers=new RemoveOneItemWithTimeout[10]; int num_dead=0; for(int i=0; i < removers.length; i++) { removers[i]=new RemoveOneItemWithTimeout(i, 15000, queue); removers[i].start(); } System.out.println("-- adding element 99"); queue.add(new Long(99)); System.out.println("-- adding element 100"); queue.add(new Long(100)); long target_time=System.currentTimeMillis() + 10000L; do { int num_rsps=0; for(int i=0; i < removers.length; i++) { if(removers[i].getRetval() != null) num_rsps++; } if(num_rsps == 2) break; Util.sleep(500); } while(target_time > System.currentTimeMillis()); Util.sleep(3000); for(int i=0; i < removers.length; i++) { System.out.println("remover #" + i + " is " + (removers[i].isAlive() ? "alive" : "terminated")); if(!removers[i].isAlive()) { num_dead++; } } assert num_dead == 2 : "num_dead should have been 2 but was " + num_dead; System.out.println("closing queue - causing all remaining threads to terminate"); queue.close(false); // will cause all threads still blocking on remove() to return Util.sleep(500); num_dead=0; for(int i=0; i < removers.length; i++) { System.out.println("remover #" + i + " is " + (removers[i].isAlive()? "alive" : "terminated")); if(!removers[i].isAlive()) { num_dead++; } } assert num_dead == 10 : "num_dead should have been 10 but was " + num_dead; } /** Multiple threads add one element, one thread read them all. * (the one that has the element) */ public static void testMultipleWriterOneReader() throws QueueClosedException { final Queue queue=new Queue(); AddOneItem[] adders=new AddOneItem[10]; int num_dead=0; int num_items=0; int items=1000; for(int i=0; i < adders.length; i++) { adders[i]=new AddOneItem(i, items, queue); adders[i].start(); } Util.sleep(500); while(num_items < (adders.length * items)) { queue.remove(); num_items++; } Util.sleep(1000); for(int i=0; i < adders.length; i++) { System.out.println("adder #" + i + " is " + (adders[i].isAlive()? "alive" : "terminated")); if(!adders[i].isAlive()) { num_dead++; } } assert num_dead == 10 : "num_dead should have been 10 but was " + num_dead; queue.close(false); // will cause all threads still blocking on peek() to return } /** * Times how long it takes to add and remove 1000000 elements concurrently (1 reader, 1 writer) */ public static void testConcurrentAddRemove() throws QueueClosedException { final Queue queue=new Queue(); final long NUM=1000000; long num_received=0; Object ret; long start, stop; start=System.currentTimeMillis(); new Thread() { public void run() { for(int i=0; i < NUM; i++) { try { queue.add(new Object()); } catch(QueueClosedException e) { } } } }.start(); while(num_received < NUM) { ret=queue.remove(); if(ret != null) num_received++; } assert num_received == NUM; stop=System.currentTimeMillis(); System.out.println("time to add/remove " + NUM + " elements: " + (stop-start)); } /** Has multiple threads add(), remove() and peek() elements to/from the queue */ public static void testConcurrentAccess() { final Queue queue=new Queue(); final int NUM_THREADS=10; final int INTERVAL=5000; Writer[] writers=new Writer[NUM_THREADS]; Reader[] readers=new Reader[NUM_THREADS]; int[] writes=new int[NUM_THREADS]; int[] reads=new int[NUM_THREADS]; long total_reads=0, total_writes=0; for(int i=0; i < writers.length; i++) { readers[i]=new Reader(i, reads, queue); readers[i].start(); writers[i]=new Writer(i, writes, queue); writers[i].start(); } Util.sleep(INTERVAL); System.out.println("current queue size=" + queue.size()); for(int i=0; i < writers.length; i++) { writers[i].stopThread(); } for(int i=0; i < readers.length; i++) { readers[i].stopThread(); } queue.close(false); // will cause all threads still blocking on peek() to return System.out.println("current queue size=" + queue.size()); for(int i=0; i < writers.length; i++) { try { writers[i].join(300); readers[i].join(300); } catch(Exception ex) { System.err.println(ex); } } for(int i=0; i < writes.length; i++) { System.out.println("Thread #" + i + ": " + writes[i] + " writes, " + reads[i] + " reads"); total_writes+=writes[i]; total_reads+=reads[i]; } System.out.println("total writes=" + total_writes + ", total_reads=" + total_reads + ", diff=" + Math.abs(total_writes - total_reads)); } static class AddOneItem extends Thread { Long retval=null; int rank=0; int iteration=0; Queue queue; AddOneItem(int rank, int iteration, Queue queue) { super("AddOneItem thread #" + rank); this.rank=rank; this.iteration=iteration; setDaemon(true); this.queue=queue; } public void run() { try { for(int i=0; i < iteration; i++) { queue.add(new Long(rank)); // Util.sleepRandom(1); // System.out.println("Thread #" + rank + " added element (" + rank + ")"); } } catch(QueueClosedException closed) { System.err.println("Thread #" + rank + ": queue was closed"); } } } static class RemoveOneItem extends Thread { Long retval=null; int rank=0; Queue queue; RemoveOneItem(int rank, Queue queue) { super("RemoveOneItem thread #" + rank); this.rank=rank; this.queue=queue; setDaemon(true); } public void run() { try { retval=(Long)queue.remove(); // System.out.println("Thread #" + rank + " removed element (" + retval + ")"); } catch(QueueClosedException closed) { System.err.println("Thread #" + rank + ": queue was closed"); } } Long getRetval() { return retval; } } static class RemoveOneItemWithTimeout extends Thread { Long retval=null; final int rank; final long timeout; final Queue queue; RemoveOneItemWithTimeout(int rank, long timeout, Queue queue) { super("RemoveOneItem thread #" + rank); this.rank=rank; this.timeout=timeout; this.queue=queue; setDaemon(true); } public void run() { try { retval=(Long)queue.removeWait(timeout); System.out.println("Thread #" + rank + ": retrieved " + retval); } catch(QueueClosedException closed) { System.out.println("Thread #" + rank + ": queue was closed"); } catch(TimeoutException e) { System.out.println("Thread #" + rank + ": timeout occurred"); } } Long getRetval() { return retval; } } static class Writer extends Thread { int rank=0; int num_writes=0; boolean running=true; int[] writes=null; Queue queue; Writer(int i, int[] writes, Queue queue) { super("WriterThread"); rank=i; this.writes=writes; this.queue=queue; setDaemon(true); } public void run() { while(running) { try { queue.add(new Long(System.currentTimeMillis())); num_writes++; } catch(QueueClosedException closed) { running=false; } catch(Throwable t) { System.err.println("QueueTest.Writer.run(): exception=" + t); } } writes[rank]=num_writes; } void stopThread() { running=false; } } static class Reader extends Thread { int rank; int num_reads=0; int[] reads=null; boolean running=true; Queue queue; Reader(int i, int[] reads, Queue queue) { super("ReaderThread"); rank=i; this.reads=reads; this.queue=queue; setDaemon(true); } public void run() { Long el; while(running) { try { el=(Long)queue.remove(); if(el == null) { // @remove System.out.println("QueueTest.Reader.run(): peek() returned null element. " + "queue.size()=" + queue.size() + ", queue.closed()=" + queue.closed()); } assert el != null; num_reads++; } catch(QueueClosedException closed) { running=false; } catch(Throwable t) { System.err.println("QueueTest.Reader.run(): exception=" + t); } } reads[rank]=num_reads; } void stopThread() { running=false; } } }