/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/
package kilim.test;
import java.util.HashMap;
import java.util.HashSet;
import junit.framework.TestCase;
import kilim.Mailbox;
import kilim.Pausable;
import kilim.Task;
public class TestMailbox extends TestCase {
public void testThread() {
final Mailbox<Msg> mb = new Mailbox<Msg>();
final int nThreads = 30;
final int nTimes = 1000;
final int nMsgs = nThreads * nTimes;
for (int i = 0; i < nThreads ; i++) {
final int id = i;
new Thread() {
public void run() {
for (int j = 0; j < nTimes; j++) {
mb.putnb(new Msg(id, j));
Thread.yield();
}
}
}.start();
}
HashMap<Integer, Integer> lastRcvd = new HashMap<Integer,Integer>();
int nNewThreads = 0;
for (int i = 0; i < nMsgs; i++) {
Msg m = mb.getb();
// assert that the number received is one more than the last received
// from that thread.
Integer last = lastRcvd.put(m.tid, m.num);
if (last == null) {
nNewThreads++;
} else {
assertTrue(m.num == last.intValue() + 1);
}
}
assertTrue(nNewThreads == nThreads);
// Make sure we have heard from all threads
assertTrue(lastRcvd.keySet().size() == nThreads);
int lastVal = nTimes - 1;
for(Integer tid : lastRcvd.keySet()) {
Integer v = lastRcvd.get(tid);
assertTrue(v != null);
assertTrue(v.intValue() == lastVal);
}
try {Thread.sleep(1000);} catch (InterruptedException ignore) {}
// Make sure there are no extra messages floating around.
assertTrue(mb.getnb() == null);
}
public void testSimpleTask_NotPausing() {
final int numMsgs = 100;
Mailbox<Msg> mainmb = new Mailbox<Msg>();
TaskMB_NoPause t = new TaskMB_NoPause(mainmb, numMsgs);
t.start();
for (int i = 0; i < numMsgs ; i++) {
Msg m = mainmb.getb(1000);
assertTrue(m.num == i);
}
}
public void testSimpleTask_Pausing() {
Mailbox<Msg> mainmb = new Mailbox<Msg>();
final int nTasks = 1;
final int nTimes = 1000;
final int nMsgs = nTasks * nTimes;
for (int i = 0; i < nTasks ; i++) {
TaskMB t = new TaskMB(mainmb);
t.start();
t.mymb.putnb(new Msg(i, nTimes));
}
HashMap<Integer, Integer> lastRcvd = new HashMap<Integer,Integer>();
int nNewThreads = 0;
for (int i = 0; i < nMsgs; i++) {
Msg m = mainmb.getb();
// assert that the number received is one more than the last received
// from that thread.
Integer last = lastRcvd.put(m.tid, m.num);
if (last == null) {
nNewThreads++;
} else {
assertTrue(m.num == last.intValue() + 1);
}
}
assertTrue(nNewThreads == nTasks);
// Make sure we have heard from all threads
assertTrue(lastRcvd.keySet().size() == nTasks);
int lastVal = nTimes - 1;
for(Integer tid : lastRcvd.keySet()) {
Integer v = lastRcvd.get(tid);
assertTrue(v != null);
assertTrue(v.intValue() == lastVal);
}
try {Thread.sleep(1000);} catch (InterruptedException ignore) {}
// Make sure there are no extra messages floating around.
assertTrue(mainmb.getnb() == null);
}
public void testMbxBounds() {
Mailbox<Msg> mainmb = new Mailbox<Msg>(2, 2);
TaskMB t = new TaskMB(mainmb);
t.start();
t.mymb.putnb(new Msg(1, 100));
for (int i = 0; i < 100; i++) {
if (i % 5 == 0) {
// Every so often, make sure that the task is forced to block on put, by delaying draining the mbx
try {Thread.sleep(100);} catch (InterruptedException ignore) {}
}
mainmb.getb();
}
try {Thread.sleep(500);} catch (InterruptedException ignore) {}
assertTrue(mainmb.getnb() == null);
}
public void testTasks() {
Mailbox<Msg> mb = new Mailbox<Msg>();
final int nTasks = 100;
final int nTimes = 1000;
final int nMsgs = nTasks * nTimes;
for (int i = 0; i < nTasks ; i++) {
TaskMB t = new TaskMB(/*mainmb=*/mb);
t.start();
t.mymb.putnb(new Msg(i, nTimes));
}
HashMap<Integer, Integer> lastRcvd = new HashMap<Integer,Integer>();
int nNewThreads = 0;
for (int i = 0; i < nMsgs; i++) {
Msg m = mb.getb();
// assert that the number received is one more than the last received
// from that thread.
Integer last = lastRcvd.put(m.tid, m.num);
if (last == null) {
nNewThreads++;
} else {
assertTrue(m.num == last.intValue() + 1);
}
}
assertTrue(nNewThreads == nTasks);
// Make sure we have heard from all threads
assertTrue(lastRcvd.keySet().size() == nTasks);
int lastVal = nTimes - 1;
for(Integer tid : lastRcvd.keySet()) {
Integer v = lastRcvd.get(tid);
assertTrue(v != null);
assertTrue(v.intValue() == lastVal);
}
try {Thread.sleep(1000);} catch (InterruptedException ignore) {}
// Make sure there are no extra messages floating around.
assertTrue(mb.getnb() == null);
}
// Send messages on two mailboxes and collect them back on one mailbox.
public void testSelectSimple() {
Mailbox<Msg> mainmb = new Mailbox<Msg>();
SelectTaskMB t = new SelectTaskMB(mainmb);
t.start();
// Make sure the task is blocked on select and hasn't already
// sent us a message
try {Thread.sleep(100);} catch (InterruptedException ignore) {}
assertTrue(! mainmb.hasMessage());
HashSet<Msg> sentMsgs = new HashSet<Msg>();
final int n = 10;
for (int i = 0; i < n; i++) {
Msg m = new Msg();
assertTrue(t.mymb2.putnb(m));
sentMsgs.add(m);
try {Thread.sleep(10);} catch (InterruptedException ignore) {}
m = new Msg();
assertTrue(t.mymb1.putnb(m));
sentMsgs.add(m);
}
for (int i = 0; i < n*2; i++) {
Msg m = mainmb.getb(1000);
assertTrue(m != null && sentMsgs.contains(m));
}
}
}
class Msg {
int tid; // thread or task id
int num;
Msg(){}
Msg(int id, int n) {tid = id; num = n;}
public String toString() {
return "Msg(" + tid + "," + num + ")";
}
};
class TaskMB extends Task {
Mailbox<Msg> mymb;
Mailbox<Msg> mainmb;
TaskMB(Mailbox<Msg> ms) {
mymb = new Mailbox<Msg>();
mainmb = ms;
}
public void execute() throws Pausable {
Msg m = mymb.get();
assert m != null : "task rcvd null msg";
int id = m.tid; // Receive this task's id
int n = m.num; // Receive the number of times we have to loop
for (int i = 0; i < n; i++) {
mainmb.put(new Msg(id, i));
if (i % 10 == 0) {
Task.yield();
}
}
}
}
/**
* A Task that only makes nonpausing calls.
* @author s
*/
class TaskMB_NoPause extends Task {
Mailbox<Msg> mainmb;
int numMsgs;
TaskMB_NoPause(Mailbox<Msg> ms, int numMsgs) {
mainmb = ms;
this.numMsgs = numMsgs;
}
public void execute() throws Pausable {
int n = numMsgs;
for (int i = 0; i < n; i++) {
mainmb.putnb(new Msg(id, i));
}
}
}
class SelectTaskMB extends Task {
Mailbox<Msg> mymb1, mymb2;
Mailbox<Msg> mainmb;
SelectTaskMB(Mailbox<Msg> mb) {
mymb1 = new Mailbox<Msg>();
mymb2 = new Mailbox<Msg>();
mainmb = mb;
}
public void execute() throws Pausable {
while (true) {
Msg m;
// Receive a message on mymb1 or 2 and forward to mainmb
// If some error, send a dummy message and testSelect()
// will flag an error.
switch (Mailbox.select(mymb1, mymb2)) {
case 0:
m = mymb1.getnb();
mainmb.put(m);
break;
case 1:
m = mymb2.getnb();
if (m == null) m = new Msg();
mainmb.put(m);
break;
default:
mainmb.put(new Msg());
}
}
}
}