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;
31 private boolean disposed = false;
33 private Runnable scheduleSyncExec = new Runnable() {
36 beginSyncExec.release();
38 endSyncExec.acquire();
39 } catch (InterruptedException e) {
44 protected StandardRealm(Engine engine, String id) {
47 this.nodeManager = createManager();
50 protected StandardNodeManager<Node, Engine> createManager(Node root) {
51 return new StandardNodeManager<Node, Engine>(this, root);
54 protected StandardNodeManager<Node, Engine> createManager() {
55 return createManager(createRootNode());
59 * For compatibility reasons. Existing implementations implement createManager() directly and in that case this is not needed.
60 * New implementations should not implement createManager() but rather implement this.
62 protected Node createRootNode() {
63 throw new UnsupportedOperationException();
66 protected String getSCLContextKey() {
67 return getClass().getSimpleName();
70 public String getId() {
74 public Engine getEngine() {
78 public Thread getThread() {
79 return executorThread;
82 @SuppressWarnings({ "rawtypes", "unchecked" })
83 public Object syncExec(Function fun) throws InterruptedException {
84 executor.execute(scheduleSyncExec);
85 SCLContext context = SCLContext.getCurrent();
86 Engine oldConnection = (Engine)context.put(getSCLContextKey(), engine);
89 beginSyncExec.acquire();
90 Thread oldThread = executorThread;
91 executorThread = Thread.currentThread();
93 return fun.apply(Tuple0.INSTANCE);
95 executorThread = oldThread;
96 endSyncExec.release();
99 context.put(getSCLContextKey(), oldConnection);
103 @SuppressWarnings("rawtypes")
104 public void asyncExec(final Function fun) {
105 executor.execute(new Runnable() {
106 @SuppressWarnings("unchecked")
109 SCLContext context = SCLContext.getCurrent();
110 context.put(getSCLContextKey(), engine);
111 fun.apply(Tuple0.INSTANCE);
117 public void syncExec(Runnable runnable) throws InterruptedException {
118 if(executorThread == Thread.currentThread()) {
121 } catch (Throwable t) {
122 LOGGER.error("Error executing runnable in realm", t);
128 executor.execute(scheduleSyncExec);
130 beginSyncExec.acquire();
131 Thread oldThread = executorThread;
132 executorThread = Thread.currentThread();
135 } catch (Throwable t) {
136 LOGGER.error("Error executing runnable in realm", t);
138 executorThread = oldThread;
139 endSyncExec.release();
144 public void asyncExec(Runnable runnable) {
145 if(executorThread == Thread.currentThread()) {
148 } catch (Throwable t) {
149 LOGGER.error("Error executing runnable in realm", t);
155 executor.execute(runnable);
158 public void close() {
161 if (!executor.awaitTermination(500L, TimeUnit.MILLISECONDS)) {
162 List<Runnable> runnablesLeft = executor.shutdownNow();
163 if (!runnablesLeft.isEmpty()) {
164 getLogger().info("Runnables left for realm " + this + " after executor shutdown! " + runnablesLeft);
167 } catch (InterruptedException e) {
168 getLogger().info("Could not shutdown executor " + executor + " for realm " + this, e);
173 // Should never be true
174 if (!executorThread.isAlive())
175 executorThread.interrupt();
176 executorThread = null;
187 public boolean isDisposed() {
191 public StandardNodeManager<Node, Engine> getNodeManager() {
195 public abstract org.slf4j.Logger getLogger();
197 private void setExecutorThread(Thread t) {
201 private static class StandardRealmThreadFactory implements ThreadFactory {
203 private StandardRealm<?, ?> realm;
205 public StandardRealmThreadFactory(StandardRealm<?, ?> realm) {
210 public Thread newThread(Runnable r) {
211 Thread t = new Thread(r);
212 realm.setExecutorThread(t);