-
-
Save IvayloDankolov/338df53976d6745f02448e5088ace6e5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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