Skip to content

Instantly share code, notes, and snippets.

@IvayloDankolov
Created April 28, 2016 00:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IvayloDankolov/338df53976d6745f02448e5088ace6e5 to your computer and use it in GitHub Desktop.
Save IvayloDankolov/338df53976d6745f02448e5088ace6e5 to your computer and use it in GitHub Desktop.
public class LockstepThread {
private static final ThreadGroup workerGroup = new ThreadGroup("JavaScriptCore workers");
private enum MessageKind {
EXECUTE_FUNC,
RETURN_RESULT,
DIE
}
private static class Message {
public Message(MessageKind kind, Object data) {
this.kind = kind;
this.data = data;
}
public final MessageKind kind;
public final Object data;
}
private final BlockingQueue<Message> backgroundQueue;
private final BlockingQueue<Message> foregroundQueue;
public LockstepThread() {
backgroundQueue = new SynchronousQueue<>(true);
foregroundQueue = new SynchronousQueue<>(true);
backgroundThread = new Thread(workerGroup, threadRunnable, "JavaScriptCore worker", 2000000);
backgroundThread.start();
}
private final Thread backgroundThread;
@SuppressWarnings("FieldCanBeLocal")
private final Runnable threadRunnable = new Runnable() {
@Override
public void run() {
try {
runLoop(backgroundQueue, foregroundQueue);
} catch (InterruptedException unused) {
Log.i("JavaScriptCore", "Background thread interrupted");
}
ArrayList<Message> remainingMessages = new ArrayList<>();
backgroundQueue.drainTo(remainingMessages);
//Should be empty at this point unless we interrupted the thread
for(Message msg: remainingMessages) {
if(msg.kind == MessageKind.EXECUTE_FUNC) {
foregroundQueue.add(new Message(MessageKind.RETURN_RESULT, null));
}
}
}
};
public Object runLoop(BlockingQueue<Message> incoming, BlockingQueue<Message> outgoing) throws InterruptedException {
while(true) {
Message msg = incoming.take();
String thread = (Thread.currentThread() == backgroundThread)?"Background":"Main";
Log.i("JavaScriptCore", "Lockstep [" + thread + "]: Receive " + msg.kind.toString());
switch(msg.kind) {
case EXECUTE_FUNC:
Functor<?> functor = (Functor<?>) msg.data;
Object result = functor.call();
Message resultMsg = new Message(MessageKind.RETURN_RESULT, result);
outgoing.put(resultMsg);
break;
case RETURN_RESULT:
return msg.data;
case DIE:
return null;
}
}
}
public <T> T defer(Functor<T> functor) {
String thread = (Thread.currentThread() == backgroundThread)?"Background":"Main";
Log.i("JavaScriptCore", "Lockstep ["+thread+"]: Defer");
BlockingQueue<Message> source, dest;
if( Thread.currentThread().equals(backgroundThread) ) {
source = backgroundQueue;
dest = foregroundQueue;
} else {
source = foregroundQueue;
dest = backgroundQueue;
}
try {
Log.i("JavaScriptCore", "Lockstep [" + thread + "]: Send " + MessageKind.EXECUTE_FUNC.toString());
dest.put(new Message(MessageKind.EXECUTE_FUNC, functor));
//TODO: Pass Class<T> around, we don't want to completely ignore types
@SuppressWarnings("unchecked")
T result = (T)runLoop(source, dest);
return result;
} catch (InterruptedException e) {
Log.w("JavaScriptCore", "Interrupted while deferring");
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment