This repository has been archived on 2023-11-26. You can view files and clone it, but cannot push or open issues or pull requests.
ScriptCraft/scriptcraft/src/main/java/com/thebrokenrail/scriptcraft/core/quickjs/QuickJSManager.java

187 lines
5.9 KiB
Java

package com.thebrokenrail.scriptcraft.core.quickjs;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class QuickJSManager {
private static QuickJSNative quickjs;
public static abstract class Task {
private boolean lastTask = false;
private boolean done = false;
private boolean err;
private Object obj;
protected abstract Object run(QuickJSNative quickjs) throws JSException;
}
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: " + task.obj);
} 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 {
quickjs = new QuickJSNative();
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(() -> {
started.set(false);
Task task = new Task() {
@Override
protected Object run(QuickJSNative quickjs) {
System.out.println("Freeing QuickJS");
quickjs.free();
return null;
}
};
task.lastTask = true;
sendTaskToQuickJS(task);
}));
}
}
private static final AtomicBoolean lock = new AtomicBoolean(false);
public static synchronized Object sendTaskToQuickJS(Task task) {
if (!started.get()) {
return null;
}
synchronized (lock) {
if (lock.get()) {
throw new RuntimeException();
} else {
lock.set(true);
quickJSOutputTask.set(null);
quickJSInputTask.set(task);
lock.notifyAll();
if (task.lastTask) {
return null;
} else {
do {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
Task outputTask = quickJSOutputTask.get();
if (outputTask != null && !outputTask.done) {
try {
outputTask.obj = outputTask.run(null);
outputTask.err = false;
} catch (Throwable e) {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
outputTask.obj = writer.toString();
outputTask.err = true;
}
outputTask.done = true;
quickJSOutputTask.set(outputTask);
}
lock.notifyAll();
} while (!task.done);
}
lock.set(false);
return task.obj;
}
}
}
}