2020-04-28 00:15:24 +00:00
|
|
|
package com.thebrokenrail.scriptcraft.core.quickjs;
|
2020-04-25 13:33:17 +00:00
|
|
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
|
|
|
|
public class QuickJSManager {
|
2020-04-28 00:15:24 +00:00
|
|
|
private static QuickJSNative quickjs;
|
2020-04-25 13:33:17 +00:00
|
|
|
|
|
|
|
public static abstract class Task {
|
|
|
|
private boolean lastTask = false;
|
|
|
|
private boolean done = false;
|
|
|
|
private boolean err;
|
|
|
|
private Object obj;
|
|
|
|
|
2020-04-28 00:15:24 +00:00
|
|
|
protected abstract Object run(QuickJSNative quickjs) throws JSException;
|
2020-04-25 13:33:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private static Thread thread;
|
|
|
|
|
|
|
|
private static final AtomicReference<Task> quickJSInputTask = new AtomicReference<>();
|
|
|
|
|
|
|
|
private static final AtomicReference<Task> quickJSOutputTask = new AtomicReference<>();
|
|
|
|
|
|
|
|
public static Object sendTaskFromQuickJS(Task task) {
|
|
|
|
if (Thread.currentThread() != thread) {
|
|
|
|
throw new RuntimeException();
|
|
|
|
}
|
|
|
|
task.done = false;
|
|
|
|
task.err = false;
|
|
|
|
synchronized (lock) {
|
|
|
|
quickJSOutputTask.set(task);
|
|
|
|
lock.notifyAll();
|
|
|
|
while (!task.done) {
|
|
|
|
try {
|
|
|
|
lock.wait();
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (task.err) {
|
|
|
|
throw new RuntimeException("Java Exception While Executing Task");
|
|
|
|
} else {
|
|
|
|
return task.obj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final Object startedLock = new Object();
|
|
|
|
|
|
|
|
private static class QuickJSThread extends Thread {
|
|
|
|
private QuickJSThread() {
|
|
|
|
super("QuickJS thread");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (lock) {
|
|
|
|
init();
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
try {
|
|
|
|
lock.wait();
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
Task task = quickJSInputTask.get();
|
|
|
|
if (task != null && !task.done) {
|
|
|
|
try {
|
|
|
|
task.obj = task.run(quickjs);
|
|
|
|
} catch (Throwable e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
task.obj = null;
|
|
|
|
}
|
|
|
|
task.done = true;
|
|
|
|
if (task.lastTask) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lock.notifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void init() {
|
|
|
|
try {
|
2020-04-28 00:15:24 +00:00
|
|
|
quickjs = new QuickJSNative();
|
2020-04-25 13:33:17 +00:00
|
|
|
synchronized (startedLock) {
|
|
|
|
startedLock.notifyAll();
|
|
|
|
}
|
|
|
|
} catch (JSException e) {
|
|
|
|
started.set(false);
|
|
|
|
synchronized (startedLock) {
|
|
|
|
startedLock.notifyAll();
|
|
|
|
}
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final AtomicBoolean started = new AtomicBoolean(false);
|
|
|
|
|
|
|
|
public static void init(Task initTask) {
|
|
|
|
if (!started.get()) {
|
|
|
|
started.set(true);
|
|
|
|
|
|
|
|
thread = new QuickJSThread();
|
|
|
|
|
|
|
|
synchronized (startedLock) {
|
|
|
|
thread.start();
|
|
|
|
try {
|
|
|
|
startedLock.wait();
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!started.get()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendTaskToQuickJS(initTask);
|
|
|
|
|
|
|
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
2020-04-26 17:23:16 +00:00
|
|
|
started.set(false);
|
2020-04-25 13:33:17 +00:00
|
|
|
Task task = new Task() {
|
|
|
|
@Override
|
2020-04-28 00:15:24 +00:00
|
|
|
protected Object run(QuickJSNative quickjs) {
|
2020-04-25 13:33:17 +00:00
|
|
|
System.out.println("Freeing QuickJS");
|
|
|
|
quickjs.free();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
task.lastTask = true;
|
|
|
|
sendTaskToQuickJS(task);
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 00:36:32 +00:00
|
|
|
private static final AtomicBoolean lock = new AtomicBoolean(false);
|
2020-04-25 13:33:17 +00:00
|
|
|
|
2020-05-05 00:36:32 +00:00
|
|
|
public static synchronized Object sendTaskToQuickJS(Task task) {
|
2020-04-25 13:33:17 +00:00
|
|
|
if (!started.get()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
synchronized (lock) {
|
2020-05-05 00:36:32 +00:00
|
|
|
if (lock.get()) {
|
|
|
|
throw new RuntimeException();
|
2020-04-25 13:33:17 +00:00
|
|
|
} else {
|
2020-05-05 00:36:32 +00:00
|
|
|
lock.set(true);
|
|
|
|
quickJSOutputTask.set(null);
|
|
|
|
quickJSInputTask.set(task);
|
|
|
|
lock.notifyAll();
|
|
|
|
if (task.lastTask) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
while (true) {
|
2020-04-25 13:33:17 +00:00
|
|
|
try {
|
2020-05-05 00:36:32 +00:00
|
|
|
lock.wait();
|
|
|
|
} catch (InterruptedException e) {
|
2020-04-25 13:33:17 +00:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2020-05-05 00:36:32 +00:00
|
|
|
Task outputTask = quickJSOutputTask.get();
|
|
|
|
if (outputTask != null && !outputTask.done) {
|
|
|
|
try {
|
|
|
|
outputTask.obj = outputTask.run(null);
|
|
|
|
outputTask.err = false;
|
|
|
|
} catch (Throwable e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
outputTask.obj = null;
|
|
|
|
outputTask.err = true;
|
|
|
|
}
|
|
|
|
outputTask.done = true;
|
|
|
|
quickJSOutputTask.set(outputTask);
|
|
|
|
}
|
|
|
|
lock.notifyAll();
|
|
|
|
if (task.done) {
|
|
|
|
break;
|
|
|
|
}
|
2020-04-25 13:33:17 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-05 00:36:32 +00:00
|
|
|
lock.set(false);
|
|
|
|
return task.obj;
|
2020-04-25 13:33:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|