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 protected StandardNodeManager<Node, Engine> createManager(Node root) {
50 return new StandardNodeManager<Node, Engine>(this, root);
53 protected StandardNodeManager<Node, Engine> createManager() {
54 return createManager(createRootNode());
58 * For compatibility reasons. Existing implementations implement createManager() directly and in that case this is not needed.
59 * New implementations should not implement createManager() but rather implement this.
61 protected Node createRootNode() {
62 throw new UnsupportedOperationException();
65 protected String getSCLContextKey() {
66 return getClass().getSimpleName();
69 public String getId() {
73 public Engine getEngine() {
77 public Thread getThread() {
78 return executorThread;
81 @SuppressWarnings({ "rawtypes", "unchecked" })
82 public Object syncExec(Function fun) throws InterruptedException {
83 executor.execute(scheduleSyncExec);
84 SCLContext context = SCLContext.getCurrent();
85 Engine oldConnection = (Engine)context.put(getSCLContextKey(), engine);
88 beginSyncExec.acquire();
89 Thread oldThread = executorThread;
90 executorThread = Thread.currentThread();
92 return fun.apply(Tuple0.INSTANCE);
94 executorThread = oldThread;
95 endSyncExec.release();
98 context.put(getSCLContextKey(), oldConnection);
102 @SuppressWarnings("rawtypes")
103 public void asyncExec(final Function fun) {
104 executor.execute(new Runnable() {
105 @SuppressWarnings("unchecked")
108 SCLContext context = SCLContext.getCurrent();
109 context.put(getSCLContextKey(), engine);
110 fun.apply(Tuple0.INSTANCE);
116 public void syncExec(Runnable runnable) throws InterruptedException {
117 if(executorThread == Thread.currentThread()) {
120 } catch (Throwable t) {
121 LOGGER.error("Error executing runnable in realm", t);
127 executor.execute(scheduleSyncExec);
129 beginSyncExec.acquire();
130 Thread oldThread = executorThread;
131 executorThread = Thread.currentThread();
134 } catch (Throwable t) {
135 LOGGER.error("Error executing runnable in realm", t);
137 executorThread = oldThread;
138 endSyncExec.release();
143 public void asyncExec(Runnable runnable) {
144 if(executorThread == Thread.currentThread()) {
147 } catch (Throwable t) {
148 LOGGER.error("Error executing runnable in realm", t);
154 executor.execute(runnable);
157 public void close() {
160 if (!executor.awaitTermination(500L, TimeUnit.MILLISECONDS)) {
161 List<Runnable> runnablesLeft = executor.shutdownNow();
162 if (!runnablesLeft.isEmpty()) {
163 getLogger().info("Runnables left for realm " + this + " after executor shutdown! " + runnablesLeft);
166 } catch (InterruptedException e) {
167 getLogger().info("Could not shutdown executor " + executor + " for realm " + this, e);
172 // Should never be true
173 if (!executorThread.isAlive())
174 executorThread.interrupt();
175 executorThread = null;
183 public StandardNodeManager<Node, Engine> getNodeManager() {
187 public abstract org.slf4j.Logger getLogger();
189 private void setExecutorThread(Thread t) {
193 private static class StandardRealmThreadFactory implements ThreadFactory {
195 private StandardRealm<?, ?> realm;
197 public StandardRealmThreadFactory(StandardRealm<?, ?> realm) {
202 public Thread newThread(Runnable r) {
203 Thread t = new Thread(r);
204 realm.setExecutorThread(t);