package org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * Verifies that messages sent by P are delivered in the order in which P sent them * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,singleThreaded=true) public class FifoOrderTest extends ChannelTestBase { JChannel a, b, c; CountDownLatch latch; final static int NUM=25, EXPECTED=NUM * 3; final static long SLEEPTIME=50; @BeforeMethod void setUp() throws Exception { latch=new CountDownLatch(1); a=createChannel(true,3, "A"); b=createChannel(a, "B"); c=createChannel(a, "C"); } @AfterMethod protected void tearDown() throws Exception {Util.close(c,b,a);} public void testFifoDelivery() throws Exception { long start, stop, diff; MyReceiver r1=new MyReceiver("R1"), r2=new MyReceiver("R2"), r3=new MyReceiver("R3"); a.setReceiver(r1); b.setReceiver(r2); c.setReceiver(r3); a.connect("FifoOrderTest"); b.connect("FifoOrderTest"); c.connect("FifoOrderTest"); Util.waitUntilAllChannelsHaveSameView(10000, 1000, a, b, c); new Thread(new Sender(a)) {}.start(); new Thread(new Sender(b)) {}.start(); new Thread(new Sender(c)) {}.start(); Util.sleep(500); latch.countDown(); // start senders start=System.currentTimeMillis(); for(int i=0; i < 60; i++) { System.out.println("r1: " + r1.size() + ", r2: " + r2.size() + ", r3: " + r3.size()); if(r1.size() == EXPECTED && r2.size() == EXPECTED && r3.size() == EXPECTED) break; Util.sleep(500); } stop=System.currentTimeMillis(); diff=stop - start; System.out.println("Total time: " + diff + " ms\n"); assert r1.size() == EXPECTED; assert r2.size() == EXPECTED; assert r3.size() == EXPECTED; checkFIFO(r1); checkFIFO(r2); checkFIFO(r3); } private static void checkFIFO(MyReceiver r) { Map<Address,List<Integer>> map=r.getMessages(); boolean fifo=true; List<Address> incorrect_receivers=new LinkedList<>(); System.out.println("Checking FIFO for " + r.getName() + ":"); for(Map.Entry<Address,List<Integer>> addressListEntry : map.entrySet()) { List<Integer> list=addressListEntry.getValue(); print(addressListEntry.getKey(), list); if(!verifyFIFO(list)) { fifo=false; incorrect_receivers.add(addressListEntry.getKey()); } } System.out.print("\n"); if(!fifo) assert false : "the following receivers didn't receive all messages in FIFO order: " + incorrect_receivers; } private static boolean verifyFIFO(List<Integer> list) { List<Integer> list2=new LinkedList<>(list); Collections.sort(list2); return list.equals(list2); } private static void print(Address addr, List<Integer> list) { StringBuilder sb=new StringBuilder(); sb.append(addr).append(": "); for(Integer i: list) sb.append(i).append(" "); System.out.println(sb); } private class Sender implements Runnable { final JChannel ch; final Address local_addr; public Sender(JChannel ch) { this.ch=ch; local_addr=ch.getAddress(); } public void run() { Message msg; try { latch.await(); } catch(Throwable t) { return; } for(int i=1; i <= NUM; i++) { msg=new Message(null, i); try { ch.send(msg); } catch(Exception e) { e.printStackTrace(); } } } } protected static class MyReceiver extends ReceiverAdapter { final String name; final ConcurrentMap<Address,List<Integer>> msgs=new ConcurrentHashMap<>(); AtomicInteger count=new AtomicInteger(0); public MyReceiver(String name) { this.name=name; } public void receive(Message msg) { Util.sleep(SLEEPTIME); Address sender=msg.getSrc(); List<Integer> list=msgs.get(sender); if(list == null) { list=new LinkedList<>(); List<Integer> tmp=msgs.putIfAbsent(sender, list); if(tmp != null) list=tmp; } Integer num=msg.getObject(); list.add(num); // no concurrent access: FIFO per sender ! (No need to synchronize on list) count.incrementAndGet(); } public ConcurrentMap<Address,List<Integer>> getMessages() {return msgs;} public String getName() { return name; } public int size() {return count.get();} } }