package com.thebrokenrail.scriptcraft.core.quickjs; 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 quickJSInputTask = new AtomicReference<>(); private static final AtomicReference 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 { 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 { while (true) { 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) { e.printStackTrace(); outputTask.obj = null; outputTask.err = true; } outputTask.done = true; quickJSOutputTask.set(outputTask); } lock.notifyAll(); if (task.done) { break; } } } lock.set(false); return task.obj; } } } }