package org.jgroups.tests;
import org.jgroups.*;
import org.jgroups.protocols.TP;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests the concurrent stack (TP)
* @author Bela Ban
*/
@Test(groups=Global.STACK_DEPENDENT,sequential=true)
public class FifoOrderTest extends ChannelTestBase {
JChannel ch1, ch2, ch3;
final static int NUM=25, EXPECTED=NUM * 3;
final static long SLEEPTIME=100;
CyclicBarrier barrier;
@BeforeMethod
void setUp() throws Exception {
barrier=new CyclicBarrier(4);
ch1=createChannel(true,3, "A");
ch2=createChannel(ch1, "B");
ch3=createChannel(ch1, "C");
}
@AfterMethod
protected void tearDown() throws Exception {
Util.close(ch3, ch2, ch1);
barrier.reset();
}
@Test
public void testFifoDelivery() throws Exception {
long start, stop, diff;
modifyDefaultThreadPool(ch1);
modifyDefaultThreadPool(ch2);
modifyDefaultThreadPool(ch3);
MyReceiver r1=new MyReceiver("R1"), r2=new MyReceiver("R2"), r3=new MyReceiver("R3");
ch1.setReceiver(r1); ch2.setReceiver(r2); ch3.setReceiver(r3);
ch1.connect("FifoOrderTest");
ch2.connect("FifoOrderTest");
ch3.connect("FifoOrderTest");
View v=ch3.getView();
assert v.size() == 3 : "view is " + v;
new Thread(new Sender(ch1)) {}.start();
new Thread(new Sender(ch2)) {}.start();
new Thread(new Sender(ch3)) {}.start();
barrier.await(); // start senders
start=System.currentTimeMillis();
Exception ex=null;
try {
System.out.println("waiting for all messages to be received");
barrier.await((long)(EXPECTED * SLEEPTIME * 1.5), TimeUnit.MILLISECONDS); // wait for all receivers
}
catch(java.util.concurrent.TimeoutException e) {
ex=e;
}
stop=System.currentTimeMillis();
diff=stop - start;
System.out.println("Total time: " + diff + " ms\n");
checkFIFO(r1);
checkFIFO(r2);
checkFIFO(r3);
if(ex != null)
throw ex;
}
private void checkFIFO(MyReceiver r) {
Map<Address,List<Integer>> map=r.getMessages();
boolean fifo=true;
List<Address> incorrect_receivers=new LinkedList<Address>();
System.out.println("Checking FIFO for " + r.getName() + ":");
for(Address addr: map.keySet()) {
List<Integer> list=map.get(addr);
print(addr, list);
if(!verifyFIFO(list)) {
fifo=false;
incorrect_receivers.add(addr);
}
}
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<Integer>(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 static void modifyDefaultThreadPool(JChannel ch1) {
TP transport=ch1.getProtocolStack().getTransport();
ThreadPoolExecutor default_pool=(ThreadPoolExecutor)transport.getDefaultThreadPool();
if(default_pool != null) {
default_pool.setCorePoolSize(1);
default_pool.setMaximumPoolSize(100);
}
transport.setThreadPoolQueueEnabled(false);
}
private class Sender implements Runnable {
final Channel ch;
final Address local_addr;
public Sender(Channel ch) {
this.ch=ch;
local_addr=ch.getAddress();
}
public void run() {
Message msg;
try {
barrier.await();
}
catch(Throwable t) {
return;
}
for(int i=1; i <= NUM; i++) {
msg=new Message(null, null, new Integer(i));
try {
ch.send(msg);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
private class Pair<K,V> {
K key;
V val;
public Pair(K key, V val) {
this.key=key;
this.val=val;
}
public String toString() {
return key + "::" + val;
}
}
private class MyReceiver extends ReceiverAdapter {
String name;
final ConcurrentMap<Address,List<Integer>> msgs=new ConcurrentHashMap<Address,List<Integer>>();
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<Integer>();
List<Integer> tmp=msgs.putIfAbsent(sender, list);
if(tmp != null)
list=tmp;
}
Integer num=(Integer)msg.getObject();
list.add(num); // no concurrent access: FIFO per sender ! (No need to synchronize on list)
if(count.incrementAndGet() >= EXPECTED) {
System.out.println("[" + name + "]: received all messages (" + count.get() + ")");
try {
barrier.await();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
public ConcurrentMap<Address,List<Integer>> getMessages() {return msgs;}
public String getName() {
return name;
}
}
}