package com.ibm.jactors.test;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import com.ibm.jactors.Actor;
import com.ibm.jactors.ActorManager;
import com.ibm.jactors.DefaultActorManager;
import com.ibm.jactors.DefaultMessage;
import com.ibm.jactors.Message;
// Incomplete - ignore, quicksort not appropriate for Map/Reduce, need merge Sort instead
public class QuicksortActor extends TestableActor {
protected Map<String, Integer> completionCounts = new Hashtable<String, Integer>();
protected static int actorCount, idCount;
protected class PendingMerge {
public String id;
public Comparable pivot;
public List<Comparable> less;
public List<Comparable> more;
public List<Comparable> merge;
volatile public boolean lessNeeded, moreNeeded;
volatile public int count;
public PendingMerge(List<Comparable> less, List<Comparable> more) {
this.id = nextId();
this.less = less;
this.more = more;
this.merge = new LinkedList<Comparable>();
pendingMerges.put(id, this);
}
public PendingMerge() {
this(new LinkedList<Comparable>(), new LinkedList<Comparable>());
}
public void merge() {
merge.clear();
merge.addAll(less);
merge.add(pivot);
merge.addAll(more);
}
}
protected Map<String, PendingMerge> pendingMerges = new HashMap<String, PendingMerge>();
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected void loopBody(Message m) {
try {
ActorManager manager = getManager();
String subject = m.getSubject();
if ("sort".equals(subject)) {
doSort(m, manager);
} else if ("merge".equals(subject)) {
doMerge(m);
} else if ("sortComplete".equals(subject)) {
doSortComplete(m);
} else if ("init".equals(subject)) {
doInit(m, manager);
} else {
logger.warning("QuicksortActor:%s loopBody unknown subject: %s", getName(), subject);
}
} catch (Exception e) {
logger.error("sort exception", e);
}
}
private void doSort(Message m, ActorManager manager) {
Object[] params = (Object[]) m.getData();
Integer nest = params.length > 0 ? (Integer) params[0] : null;
List<Comparable> values = params.length > 1 ? (List<Comparable>) params[1] : null;
String id = params.length > 2 ? (String) params[2] : null;
PendingMerge pm = params.length > 3 ? (PendingMerge) params[3] : null;
if (values != null) {
logger.info("receive sort values %s(%d): %s, %s", id, nest, values);
doSortWorker(manager, nest, values, id, pm);
} else if (pm != null) {
logger.info("receive sort pm %s(%d): count=%d/%b/%b, %s<%s>%s", id, nest, pm.count, pm.lessNeeded,
pm.moreNeeded, pm.less, pm.pivot, pm.more);
if (pm.count > 0) {
if (pm.lessNeeded) {
logger.info("receive sort less %s(%d): %s, %s", id, nest, pm.less);
pm.lessNeeded = false;
pm.count--;
doSortWorker(manager, nest + 1, pm.less, pm.id, pm);
} else if (pm.moreNeeded) {
logger.info("receive sort more %s(%d): %s, %s", id, nest, pm.more);
pm.moreNeeded = false;
pm.count--;
doSortWorker(manager, nest + 1, pm.more, pm.id, pm);
}
if (pm.count == 0) {
DefaultMessage dm = new DefaultMessage("merge", new Object[] { nest, pm, id });
logger.info("send merge %s-%s(%d): %s<%s>%s", id, pm.id, nest, pm.less, pm.pivot, pm.more);
recordSend(id, manager.send(dm, this, getCategoryName()));
}
}
} else {
logger.error("receive empty sort %s(%d)", id, nest);
}
recordComplete(id, 1);
}
protected void doSortWorker(ActorManager manager, int nest, List<Comparable> values, String id, PendingMerge pm) {
sleep(1000);
if (values.size() <= 1) {
DefaultMessage dm = new DefaultMessage("sortComplete", new Object[] { nest, values, id });
logger.info("send size complete %s(%d): %s", id, nest, values);
recordSend(id, manager.send(dm, this, getCategoryName()));
} else {
pm = new PendingMerge();
Comparable pivot = values.remove(0);
pm.pivot = pivot;
for (Comparable c : values) {
if (c.compareTo(pivot) <= 0) {
pm.less.add(c);
} else {
pm.more.add(c);
}
}
if (pm.less.size() > 0) {
pm.lessNeeded = true;
pm.count++;
}
if (pm.more.size() > 0) {
pm.moreNeeded = true;
pm.count++;
}
logger.info("send pm sort %s(%d): count=%d, %s<%s>%s", id, nest, pm.count, pm.less, pm.pivot, pm.more);
if (pm.count > 0) {
if (pm.lessNeeded) {
DefaultMessage lm = new DefaultMessage("sort", new Object[] { nest + 1, null, pm.id, pm });
logger.info("send sort less %s: %s", pm.id, pm.less);
recordSend(id, manager.send(lm, this, getCategoryName()));
}
if (pm.moreNeeded) {
DefaultMessage mm = new DefaultMessage("sort", new Object[] { nest + 1, null, pm.id, pm });
logger.info("send sort more %s: %s", pm.id, pm.more);
recordSend(id, manager.send(mm, this, getCategoryName()));
}
}
}
}
protected void doMerge(Message m) {
sleep(1000);
Object[] params = (Object[]) m.getData();
Integer nest = params.length > 0 ? (Integer) params[0] : null;
PendingMerge pm = params.length > 1 ? (PendingMerge) params[1] : null;
String id = params.length > 2 ? (String) params[2] : null;
pm.merge();
logger.info("merge %s(%d): %s <= %s<%s>%s", pm.id, nest, pm.merge, pm.less, pm.pivot, pm.more);
DefaultMessage dm = new DefaultMessage("sortComplete", new Object[] { nest, pm.merge, pm.id });
logger.info("send sort complete %s(%d)", pm.id, nest);
recordSend(pm.id, manager.send(dm, this, getCategoryName()));
recordComplete(id, 1);
}
protected void doSortComplete(Message m) {
Object[] params = (Object[]) m.getData();
Integer nest = params.length > 0 ? (Integer) params[0] : null;
List<Comparable> values = params.length > 1 ? (List<Comparable>) params[1] : null;
String id = params.length > 2 ? (String) params[2] : null;
logger.info("receive complete %s(%d): %s", id, nest, values);
recordComplete(id, 1);
if (isComplete(id)) {
if (nest == 0) {
logger.trace("**** Sort %s complete: %s", id, values);
}
}
}
protected void doInit(Message m, ActorManager manager) {
Object[] params = (Object[]) m.getData();
Integer max = !isEmpty(params) ? (Integer) params[0] : 50;
// ensure enough actors exist
int count = manager.getActorCount(QuicksortActor.class);
for (int i = count; i < max; i++) {
createQuicksortActor(manager);
}
}
protected void resetPending(String id) {
setPending(id, 0);
}
protected void setPending(String id, int count) {
completionCounts.put(id, count);
// logger.info("setPending %s: %d", id, count);
}
protected int recordSend(String id, int count) {
int res = 0;
if (!completionCounts.containsKey(id)) {
resetPending(id);
}
completionCounts.put(id, res = completionCounts.get(id) + count);
// logger.info("recordSend %s: %d", id, res);
return res;
}
protected int recordComplete(String id, int count) {
int res = 0;
if (!completionCounts.containsKey(id)) {
resetPending(id);
}
res = completionCounts.get(id) - count;
if (res < 0) {
logger.error("**** recordComplete %s count < 0: %d", id, res);
} else {
completionCounts.put(id, res);
}
// logger.info("recordComplete %s: %d", id, res);
return res;
}
protected boolean isComplete(String id) {
boolean res = completionCounts.containsKey(id) ? completionCounts.get(id) <= 0 : true;
logger.info("isCommplete %s: %b", id, res);
return res;
}
protected static QuicksortActor createQuicksortActor(ActorManager manager) {
QuicksortActor a = (QuicksortActor) manager.createAndStartActor(QuicksortActor.class,
QuicksortActor.class.getSimpleName() + actorCount++);
a.setCategory(getCategoryName());
return a;
}
Random rand = new Random();
// test case
public void run(ActorManager am, int count) {
Actor a = createQuicksortActor(am);
}
public static String nextId() {
return "id_" + idCount++;
}
public static String getCategoryName() {
return "quicksortActor";
}
public static void main(String[] args) {
try {
ActorManager am = DefaultActorManager.getDefaultInstance();
QuicksortActor qa = new QuicksortActor();
qa.run(am, args.length > 0 ? Integer.parseInt(args[0]) : 1000);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}