1 package org.simantics.simulator.toolkit;
4 import java.util.concurrent.LinkedBlockingQueue;
5 import java.util.concurrent.Semaphore;
6 import java.util.concurrent.ThreadFactory;
7 import java.util.concurrent.ThreadPoolExecutor;
8 import java.util.concurrent.TimeUnit;
9 import java.util.function.Function;
11 import org.simantics.scl.runtime.SCLContext;
12 import org.simantics.scl.runtime.tuple.Tuple0;
13 import org.simantics.simulator.variable.Realm;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
17 abstract public class StandardRealm<Node, Engine extends StandardNodeManagerSupport<Node>> implements Realm {
19 private static final Logger LOGGER = LoggerFactory.getLogger(StandardRealm.class);
22 protected Thread executorThread;
23 private StandardRealmThreadFactory factory = new StandardRealmThreadFactory(this);
24 private ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
25 new LinkedBlockingQueue<>(), factory);
26 private Semaphore beginSyncExec = new Semaphore(0);
27 private Semaphore endSyncExec = new Semaphore(0);
29 private Engine engine;
30 protected StandardNodeManager<Node, Engine> nodeManager;
32 private Runnable scheduleSyncExec = new Runnable() {
35 beginSyncExec.release();
37 endSyncExec.acquire();
38 } catch (InterruptedException e) {
43 protected StandardRealm(Engine engine, String id) {
46 this.nodeManager = createManager();
49 abstract protected StandardNodeManager<Node, Engine> createManager();
51 protected String getSCLContextKey() {
52 return getClass().getSimpleName();
55 public String getId() {
59 public Engine getEngine() {
63 public Thread getThread() {
64 return executorThread;
67 @SuppressWarnings({ "rawtypes", "unchecked" })
68 public Object syncExec(Function fun) throws InterruptedException {
69 executor.execute(scheduleSyncExec);
70 SCLContext context = SCLContext.getCurrent();
71 Engine oldConnection = (Engine)context.put(getSCLContextKey(), engine);
74 beginSyncExec.acquire();
75 Thread oldThread = executorThread;
76 executorThread = Thread.currentThread();
78 return fun.apply(Tuple0.INSTANCE);
80 executorThread = oldThread;
81 endSyncExec.release();
84 context.put(getSCLContextKey(), oldConnection);
88 @SuppressWarnings("rawtypes")
89 public void asyncExec(final Function fun) {
90 executor.execute(new Runnable() {
91 @SuppressWarnings("unchecked")
94 SCLContext context = SCLContext.getCurrent();
95 context.put(getSCLContextKey(), engine);
96 fun.apply(Tuple0.INSTANCE);
102 public void syncExec(Runnable runnable) throws InterruptedException {
103 if(executorThread == Thread.currentThread()) {
106 } catch (Throwable t) {
107 LOGGER.error("Error executing runnable in realm", t);
113 executor.execute(scheduleSyncExec);
115 beginSyncExec.acquire();
116 Thread oldThread = executorThread;
117 executorThread = Thread.currentThread();
120 } catch (Throwable t) {
121 LOGGER.error("Error executing runnable in realm", t);
123 executorThread = oldThread;
124 endSyncExec.release();
129 public void asyncExec(Runnable runnable) {
130 if(executorThread == Thread.currentThread()) {
133 } catch (Throwable t) {
134 LOGGER.error("Error executing runnable in realm", t);
140 executor.execute(runnable);
143 public void close() {
146 if (!executor.awaitTermination(500L, TimeUnit.MILLISECONDS)) {
147 List<Runnable> runnablesLeft = executor.shutdownNow();
148 if (!runnablesLeft.isEmpty()) {
149 getLogger().info("Runnables left for realm " + this + " after executor shutdown! " + runnablesLeft);
152 } catch (InterruptedException e) {
153 getLogger().info("Could not shutdown executor " + executor + " for realm " + this, e);
158 // Should never be true
159 if (!executorThread.isAlive())
160 executorThread.interrupt();
161 executorThread = null;
169 public StandardNodeManager<Node, Engine> getNodeManager() {
173 public abstract org.slf4j.Logger getLogger();
175 private void setExecutorThread(Thread t) {
179 private static class StandardRealmThreadFactory implements ThreadFactory {
181 private StandardRealm<?, ?> realm;
183 public StandardRealmThreadFactory(StandardRealm<?, ?> realm) {
188 public Thread newThread(Runnable r) {
189 Thread t = new Thread(r);
190 realm.setExecutorThread(t);