From: Antti Villberg Date: Sat, 11 Jan 2020 10:54:22 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/master' into private/antti2 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fheads%2Fprivate%2Fantti2;hp=b8e4909d7723ad4bffcae324ade14d94d837e6da;p=simantics%2Fplatform.git Merge remote-tracking branch 'origin/master' into private/antti2 Conflicts: bundles/org.simantics.acorn/src/org/simantics/acorn/OperationQueue.java bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/PropertyInfo.java bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/StandardVariableBuilder.java Change-Id: I5a4cbd88743669fa4b1104ec466c0efa3aa0aa3d --- diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/OperationQueue.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/OperationQueue.java index a85aa8829..d48f9ba6d 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/OperationQueue.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/OperationQueue.java @@ -173,5 +173,4 @@ class OperationQueue { return System.nanoTime() - start; } - } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/BlockingAsyncProcedure.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/BlockingAsyncProcedure.java index 2d3e2804c..c491fb377 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/BlockingAsyncProcedure.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/BlockingAsyncProcedure.java @@ -12,64 +12,79 @@ package org.simantics.db.impl; import org.simantics.db.AsyncReadGraph; -import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; -import org.simantics.db.impl.graph.AsyncBarrierImpl; +import org.simantics.db.impl.graph.BarrierTracing; import org.simantics.db.impl.graph.ReadGraphImpl; +import org.simantics.db.impl.query.AsyncReadEntry; +import org.simantics.db.impl.query.PendingTaskSupport; import org.simantics.db.procedure.AsyncProcedure; public class BlockingAsyncProcedure implements AsyncProcedure { private static final Object NO_RESULT = new Object(); - private final Object key; - private final AsyncBarrierImpl barrier; - private final ReadGraphImpl procedureGraph; - private final AsyncProcedure procedure; + public final Object key; + public final ReadGraphImpl queryGraph; + public final ReadGraphImpl callerGraph; + public final AsyncProcedure procedure; + public PendingTaskSupport pendingTaskSupport; + public Object result = NO_RESULT; + public Throwable exception = null; - private Object result = NO_RESULT; - private Throwable exception = null; + private ReadGraphImpl queryGraph() { + return queryGraph; + } + + public BlockingAsyncProcedure(ReadGraphImpl callerGraph, AsyncReadEntry entry, AsyncProcedure procedure, Object key, boolean needsToBlock) { + + // A new graph for evaluating the query with correct parent and asyncBarrier + queryGraph = callerGraph.withParent(entry, () -> { + + dispatchProcedure(queryGraph(), callerGraph, entry, procedure, needsToBlock); + + }); + + queryGraph.asyncBarrier.inc(); - public BlockingAsyncProcedure(AsyncBarrierImpl barrier, ReadGraphImpl procedureGraph, AsyncProcedure procedure, Object key) { this.procedure = procedure; this.key = key; - this.barrier = barrier; - this.barrier.inc(); - this.procedureGraph = procedureGraph; + this.queryGraph.asyncBarrier.inc(); + this.callerGraph = callerGraph; + if (BarrierTracing.BOOKKEEPING) { + BarrierTracing.registerBAP(this); + } } @Override public void execute(AsyncReadGraph graph_, Result result) { + this.result = result; - try { - if(procedure != null) procedure.execute(procedureGraph, result); - } catch (Throwable throwable) { - Logger.defaultLogError("AsyncProcedure.execute threw for " + procedure, throwable); - } finally { - barrier.dec(); - } + queryGraph.asyncBarrier.dec(); + } @Override public void exception(AsyncReadGraph graph_, Throwable t) { + this.exception = t; - try { - if(procedure != null) procedure.exception(procedureGraph, t); - } catch (Throwable throwable) { - Logger.defaultLogError("AsyncProcedure.exception threw for " + procedure, throwable); - } finally { - barrier.dec(); - } + queryGraph.asyncBarrier.dec(); + } - + public void waitBarrier() { - barrier.waitBarrier(key, procedureGraph); + queryGraph.asyncBarrier.waitBarrier(key, queryGraph); + } + + public void dec() { + + queryGraph.asyncBarrier.dec(); + } @SuppressWarnings("unchecked") public Result get() throws DatabaseException { - barrier.waitBarrier(key, procedureGraph); + queryGraph.asyncBarrier.waitBarrier(key, queryGraph); if(exception != null) { if(exception instanceof DatabaseException) throw (DatabaseException)exception; @@ -80,10 +95,6 @@ public class BlockingAsyncProcedure implements AsyncProcedure { } - public boolean isDone() { - return barrier.get() == 0; - } - @SuppressWarnings("unchecked") public Result getResult() { return (Result)result; @@ -97,5 +108,41 @@ public class BlockingAsyncProcedure implements AsyncProcedure { public String toString() { return "." + procedure; } + + private void dispatchProcedure(ReadGraphImpl queryGraph, ReadGraphImpl parentGraph, AsyncReadEntry entry, AsyncProcedure procedure_, boolean needsToBlock) { + + AsyncProcedure procedure = entry != null ? entry : procedure_; + + ReadGraphImpl executeGraph = parentGraph.withParent(parentGraph.parent); + executeGraph.asyncBarrier.inc(); + try { + if(procedure != null) { + procedure.execute(executeGraph, get()); + } + } catch (DatabaseException e) { + if(procedure != null) procedure.exception(executeGraph, e); + exception = e; + } catch (Throwable t) { + DatabaseException dbe = new DatabaseException(t); + if(procedure != null) procedure.exception(executeGraph, dbe); + exception = dbe; + } finally { + + if (entry != null) { + assert(entry.isReady()); + // This does not throw + entry.performFromCache(executeGraph, procedure_); + } + + executeGraph.asyncBarrier.dec(); + if(needsToBlock) + executeGraph.asyncBarrier.waitBarrier(procedure, executeGraph); + } + + if (BarrierTracing.BOOKKEEPING) { + BarrierTracing.unregisterBAP(this); + } + + } } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/AsyncBarrierImpl.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/AsyncBarrierImpl.java index b94ec6f68..060fc22ae 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/AsyncBarrierImpl.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/AsyncBarrierImpl.java @@ -18,20 +18,24 @@ import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.impl.query.CacheEntry; import org.simantics.db.impl.query.QueryProcessor.AsyncBarrier; +import org.simantics.db.impl.query.QueryProcessor.SessionTask; final public class AsyncBarrierImpl extends AtomicInteger implements AsyncBarrier { private static final long serialVersionUID = 4724463372850048672L; - static final int WAIT_TIME = 600; + static final int WAIT_TIME = 60000; public static final boolean PRINT = false; final public AsyncBarrierImpl caller; + + final public Runnable callback; - public AsyncBarrierImpl(AsyncBarrierImpl caller, CacheEntry entry) { + public AsyncBarrierImpl(AsyncBarrierImpl caller, CacheEntry entry, Runnable callback) { super(0); this.caller = caller; + this.callback = callback; if (BarrierTracing.BOOKKEEPING) { BarrierTracing.trace(this, entry); } @@ -92,6 +96,10 @@ final public class AsyncBarrierImpl extends AtomicInteger implements AsyncBarrie new Exception()); } assert (count >= 0); + + if(callback != null) + callback.run(); + } } @@ -142,7 +150,7 @@ final public class AsyncBarrierImpl extends AtomicInteger implements AsyncBarrie e.printStackTrace(); } } - if(waitCount > WAIT_TIME*1000) { + if(waitCount > WAIT_TIME) { System.err.println("AsyncBarrierImpl.waitBarrier(" + request @@ -153,6 +161,11 @@ final public class AsyncBarrierImpl extends AtomicInteger implements AsyncBarrie synchronized (BarrierTracing.reverseLookup) { printReverse(this, 0); } + BarrierTracing.printBAPS(); + } + + for(SessionTask t : impl.processor.freeScheduling) { + System.err.println("Pending task:" + t); } // if(Development.DEVELOPMENT) { diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/BarrierTracing.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/BarrierTracing.java index 3ff77f6bc..3168b27c4 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/BarrierTracing.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/BarrierTracing.java @@ -5,13 +5,14 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import org.simantics.db.impl.BlockingAsyncProcedure; import org.simantics.db.impl.query.CacheEntry; import org.simantics.db.impl.query.QueryProcessor.SessionTask; public class BarrierTracing { public static final boolean BOOKKEEPING = false; - static final boolean RESTART_GUARD = BOOKKEEPING && false; + static final boolean RESTART_GUARD = BOOKKEEPING && true; public static Map tasks = new HashMap<>(); public static final HashMap> reverseLookup = new HashMap<>(); @@ -19,7 +20,31 @@ public class BarrierTracing { public static final HashMap> entryMap = new HashMap<>(); public static final HashMap restartMap = new HashMap<>(); public static final HashMap startMap = new HashMap<>(); + public static final HashMap baps = new HashMap<>(); + synchronized public static void registerBAP(BlockingAsyncProcedure bap) { + baps.put(bap, new Exception()); + } + + synchronized public static void unregisterBAP(BlockingAsyncProcedure bap) { + baps.remove(bap); + } + + synchronized public static void printBAPS() { + for(BlockingAsyncProcedure bap : baps.keySet()) { + Throwable e = baps.get(bap); + System.err.println("BlockingAsyncProcedure"); + System.err.println("-key: " + bap.key); + System.err.println("-queryGraph: " + bap.queryGraph); + System.err.println("-callerGraph: " + bap.callerGraph); + System.err.println("-procedure: " + bap.procedure); + System.err.println("-pendingTaskSupport: " + bap.pendingTaskSupport); + System.err.println("-result: " + bap.result); + System.err.println("-exception: " + bap.exception); + e.printStackTrace(); + } + } + public static void trace(AsyncBarrierImpl barrier, CacheEntry entry) { if (RESTART_GUARD) { diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/ReadGraphImpl.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/ReadGraphImpl.java index a693b2922..dcb48a0f0 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/ReadGraphImpl.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/ReadGraphImpl.java @@ -1926,9 +1926,9 @@ public class ReadGraphImpl implements AsyncReadGraph { public T syncRequest(final Read request) throws DatabaseException { assert (request != null); - ITask task = ThreadLogger.task(request); + //ITask task = ThreadLogger.task(request); T result = (T)QueryCache.runnerReadEntry(this, request, parent, null, null, true); - task.finish(); + //task.finish(); return result; } @@ -1950,10 +1950,8 @@ public class ReadGraphImpl implements AsyncReadGraph { assert (request != null); - ITask task = ThreadLogger.task(request); ListenerBase listener = procedure != null ? getListenerBase(procedure) : null; - T result = QueryCache.resultReadEntry(this, request, parent, listener, procedure); - task.finish(); + T result = (T)QueryCache.runnerReadEntry(this, request, parent, listener, procedure, true); return result; } @@ -2038,10 +2036,10 @@ public class ReadGraphImpl implements AsyncReadGraph { assert (request != null); - ITask task = ThreadLogger.task(request); + //ITask task = ThreadLogger.task(request); ListenerBase listener = getListenerBase(procedure); T result = (T)QueryCache.runnerAsyncReadEntry(this, request, parent, listener, procedure, true); - task.finish(); + //task.finish(); return result; } @@ -5160,7 +5158,11 @@ public class ReadGraphImpl implements AsyncReadGraph { assert (request != null); assert (procedure != null); - + + AsyncBarrierImpl barrier = asyncBarrier; + if(barrier != null) + barrier.inc(); + processor.scheduleNow(new SessionTask(this) { @Override @@ -5170,6 +5172,9 @@ public class ReadGraphImpl implements AsyncReadGraph { QueryCache.runnerReadEntry(ReadGraphImpl.this, request, parent, listener, procedure, false); } catch (DatabaseException e) { Logger.defaultLogError(e); + } finally { + if(barrier != null) + barrier.dec(); } } @@ -5241,29 +5246,43 @@ public class ReadGraphImpl implements AsyncReadGraph { assert (request != null); assert (procedure != null); - ITask task = ThreadLogger.task(request); + //ITask task = ThreadLogger.task(request); + + AsyncBarrierImpl barrier = asyncBarrier; + if(barrier != null) + barrier.inc(); processor.scheduleNow(new SessionTask(this) { @Override public void run0(int thread) { - try { + + if(barrier != null) + barrier.inc(); + + try { final ListenerBase listener = getListenerBase(procedure); QueryCache.runnerAsyncReadEntry(ReadGraphImpl.this, request, parent, listener, new AsyncProcedure() { @Override public void execute(AsyncReadGraph graph, T result) { - task.finish(); + //task.finish(); procedure.execute(graph, result); + if(barrier != null) + barrier.dec(); } @Override public void exception(AsyncReadGraph graph, Throwable throwable) { - task.finish(); + //task.finish(); procedure.exception(graph, throwable); + if(barrier != null) + barrier.dec(); } }, false); + if(barrier != null) + barrier.dec(); } catch (DatabaseException e) { Logger.defaultLogError(e); } @@ -5651,27 +5670,47 @@ public class ReadGraphImpl implements AsyncReadGraph { this.parentGraph = parentGraph; this.parent = parent; this.processor = support; - this.asyncBarrier = new AsyncBarrierImpl(parentGraph != null ? parentGraph.asyncBarrier : null, parent); + this.asyncBarrier = new AsyncBarrierImpl(parentGraph != null ? parentGraph.asyncBarrier : null, parent, null); } + ReadGraphImpl(ReadGraphImpl parentGraph, CacheEntry parent, QueryProcessor support, AsyncBarrierImpl asyncBarrier) { + this.parentGraph = parentGraph; + this.parent = parent; + this.processor = support; + this.asyncBarrier = asyncBarrier; + } + ReadGraphImpl(ReadGraphImpl graph, CacheEntry parent) { this(graph, parent, graph.processor); } + ReadGraphImpl(ReadGraphImpl parentGraph, CacheEntry parent, Runnable callback) { + this(parentGraph, parent, parentGraph.processor, new AsyncBarrierImpl(parentGraph != null ? parentGraph.asyncBarrier : null, parent, callback)); + } + ReadGraphImpl(ReadGraphImpl graph) { this(graph, graph.parent); } - public ReadGraphImpl withParent(CacheEntry parent) { - return new ReadGraphImpl(this, parent); + public ReadGraphImpl withParent(CacheEntry parent, Runnable callback) { + return new ReadGraphImpl(this, parent, callback); } + public ReadGraphImpl withParent(CacheEntry parent) { + return withParent(parent, null); + } + + public ReadGraphImpl syncWithParent(CacheEntry parent) { + return new ReadGraphImpl(this, parent, processor, null); + } + public ReadGraphImpl forRecompute(CacheEntry parent) { return new ReadGraphImpl(null, parent, processor); } public static ReadGraphImpl create(QueryProcessor support) { - return new ReadGraphImpl(null, null, support); + ReadGraphImpl result = new ReadGraphImpl(null, null, support); + return result; } public ReadGraphImpl newRestart(ReadGraphImpl impl) { @@ -6357,6 +6396,12 @@ public class ReadGraphImpl implements AsyncReadGraph { else return 1 + getLevelStatic(impl.parentGraph); } + public boolean isParent(ReadGraphImpl impl) { + if(impl == null) return false; + if(this == impl) return true; + return isParent(impl.parentGraph); + } + public ReadGraphImpl getTopLevelGraph() { return getTopLevelGraphStatic(this); } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/AsyncReadEntry.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/AsyncReadEntry.java index e13ecab72..0dd5730a5 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/AsyncReadEntry.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/AsyncReadEntry.java @@ -11,13 +11,13 @@ *******************************************************************************/ package org.simantics.db.impl.query; +import java.util.Collection; + import org.simantics.databoard.Bindings; import org.simantics.db.AsyncReadGraph; import org.simantics.db.DevelopmentKeys; import org.simantics.db.exception.DatabaseException; import org.simantics.db.impl.BlockingAsyncProcedure; -import org.simantics.db.impl.graph.AsyncBarrierImpl; -import org.simantics.db.impl.graph.BarrierTracing; import org.simantics.db.impl.graph.ReadGraphImpl; import org.simantics.db.impl.query.QueryProcessor.SessionTask; import org.simantics.db.procedure.AsyncProcedure; @@ -26,11 +26,12 @@ import org.simantics.utils.Development; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final public class AsyncReadEntry extends CacheEntryBase> implements AsyncProcedure { +final public class AsyncReadEntry extends CacheEntryBase> implements AsyncProcedure, IPending { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncReadEntry.class); protected AsyncRead id; + protected PendingTaskSupport pendingTaskSupport; AsyncReadEntry(AsyncRead request) { this.id = request; @@ -77,7 +78,7 @@ final public class AsyncReadEntry extends CacheEntryBase> i try { - BlockingAsyncProcedure proc = new BlockingAsyncProcedure<>(graph.asyncBarrier, graph, new AsyncProcedure() { + BlockingAsyncProcedure proc = new BlockingAsyncProcedure(graph, AsyncReadEntry.this, new AsyncProcedure() { @Override public void execute(AsyncReadGraph graph, T result) { @@ -90,10 +91,11 @@ final public class AsyncReadEntry extends CacheEntryBase> i except(t); } - }, id); - - id.perform(graph, proc); + }, id, true); + id.perform(proc.queryGraph, proc); + + proc.dec(); proc.get(); } catch (Throwable t) { @@ -152,103 +154,24 @@ final public class AsyncReadEntry extends CacheEntryBase> i } - public static T computeForEach(ReadGraphImpl graph, AsyncRead request, AsyncReadEntry entry, + public static T computeForEach(ReadGraphImpl callerGraph, AsyncRead request, AsyncReadEntry entry, AsyncProcedure procedure_, boolean needsToBlock) throws DatabaseException { - AsyncProcedure procedure = entry != null ? entry : procedure_; - - ReadGraphImpl queryGraph = graph.withParent(entry); - queryGraph.asyncBarrier.inc(); - - BlockingAsyncProcedure proc = new BlockingAsyncProcedure<>(queryGraph.asyncBarrier, graph, null, request); - - class AsyncTask extends SessionTask { - - int counter = 0; - T result; - DatabaseException exception; - - public AsyncTask(ReadGraphImpl graph) { - this(graph, 1); - } - - public AsyncTask(ReadGraphImpl graph, int pos) { - super(graph); - this.position = pos; - if(this.position < 1024) - this.position *= 2; - } - - @Override - public void run0(int thread) { - if(needsToBlock) proc.waitBarrier(); - if(proc.isDone()) { - ReadGraphImpl executeGraph = graph.withParent(graph.parent); - executeGraph.asyncBarrier.inc(); - try { - result = (T)proc.get(); - if(procedure != null) { - procedure.execute(executeGraph, result); - } - } catch (DatabaseException e) { - if(procedure != null) procedure.exception(executeGraph, e); - exception = e; - } catch (Throwable t) { - DatabaseException dbe = new DatabaseException(t); - if(procedure != null) procedure.exception(executeGraph, dbe); - exception = dbe; - } finally { - if (entry != null) { - // This does not throw - entry.performFromCache(executeGraph, procedure_); - } - executeGraph.asyncBarrier.dec(); - executeGraph.asyncBarrier.waitBarrier(procedure, executeGraph); - } - } else { - if(counter++ > 10000) { - if(BarrierTracing.BOOKKEEPING) { - AsyncBarrierImpl.printReverse(queryGraph.asyncBarrier, 2); - AsyncBarrierImpl caller = queryGraph.asyncBarrier.caller; - while(caller != null) { - System.err.println("called by " + AsyncBarrierImpl.report(caller)); - caller = caller.caller; - } - for(AsyncBarrierImpl ab : BarrierTracing.debuggerMap.keySet()) { - AsyncBarrierImpl.printReverse(ab, 2); - } - } - throw new IllegalStateException("Eternal loop in queries."); - } - graph.processor.scheduleLater(new AsyncTask(graph, position)); - } - } - - @Override - public boolean maybeReady() { - return proc.isDone(); - } - - } + BlockingAsyncProcedure proc = new BlockingAsyncProcedure(callerGraph, entry, procedure_, request, needsToBlock); try { - request.perform(queryGraph, proc); + request.perform(proc.queryGraph, proc); } finally { - queryGraph.asyncBarrier.dec(); + proc.queryGraph.asyncBarrier.dec(); } - AsyncTask task = new AsyncTask(graph); - - if(needsToBlock) task.run(0); - else if (proc.isDone()) task.run(0); - else { - graph.processor.scheduleLater(task); + if(needsToBlock) { + proc.waitBarrier(); + return proc.get(); + } else { return null; } - if(task.exception != null) throw task.exception; - else return task.result; - } @Override @@ -263,13 +186,41 @@ final public class AsyncReadEntry extends CacheEntryBase> i @Override public void execute(AsyncReadGraph graph, T result) { - setResult(result); - setReady(); + Collection tasks = null; + synchronized(this) { + setResult(result); + setReady(); + if(pendingTaskSupport != null) + tasks = pendingTaskSupport.executePending(); + } + if(tasks != null) + for(SessionTask task : tasks) + ((ReadGraphImpl)graph).processor.scheduleNow(task); } @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - except(throwable); + public synchronized void exception(AsyncReadGraph graph, Throwable throwable) { + Collection tasks = null; + synchronized(this) { + except(throwable); + if(pendingTaskSupport != null) + tasks = pendingTaskSupport.executePending(); + } + if(tasks != null) + for(SessionTask task : tasks) + ((ReadGraphImpl)graph).processor.scheduleNow(task); + } + + public void executeWhenResultIsAvailable(QueryProcessor processor, SessionTask task) { + boolean ready = false; + synchronized(this) { + if(pendingTaskSupport == null) + pendingTaskSupport = new PendingTaskSupport(this); + ready = pendingTaskSupport.executeWhenResultIsAvailable(task); + } + if(ready) { + processor.scheduleNow(task); + } } } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/IPending.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/IPending.java new file mode 100644 index 000000000..c7381353b --- /dev/null +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/IPending.java @@ -0,0 +1,7 @@ +package org.simantics.db.impl.query; + +public interface IPending { + + boolean isPending(); + +} diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/PendingTaskSupport.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/PendingTaskSupport.java new file mode 100644 index 000000000..f63bad581 --- /dev/null +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/PendingTaskSupport.java @@ -0,0 +1,45 @@ +package org.simantics.db.impl.query; + +import java.util.ArrayList; +import java.util.Collection; + +import org.simantics.db.impl.query.QueryProcessor.SessionTask; + +/* + * Support class for queuing pending tasks to be executed when result gets ready + */ +public class PendingTaskSupport { + + private ArrayList pendingTasks; + private IPending pending; + + public PendingTaskSupport(IPending pending) { + this.pending = pending; + } + + /* + * We assume here that the associated IPending performs this atomically + * The caller is responsible for execution of the returned task after the critical section + */ + public boolean executeWhenResultIsAvailable(SessionTask task) { + if(pending.isPending()) { + if(pendingTasks == null) + pendingTasks = new ArrayList(); + pendingTasks.add(task); + return false; + } else { + return true; + } + } + + /* + * We assume here that the associated IPending performs this atomically after changing the pending result + * The caller is responsible for execution of the returned task after the critical section + */ + public Collection executePending() { + ArrayList ret = pendingTasks; + pendingTasks = null; + return ret; + } + +} diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCache.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCache.java index cc0ca919b..35ebdbc65 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCache.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCache.java @@ -625,7 +625,7 @@ public class QueryCache extends QueryCacheBase { } } - ReadEntry getOrCreateReadEntry(ReadGraphImpl graph, Read r, boolean needsToBlock) throws DatabaseException { + private final ReadEntry getOrCreateReadEntry(ReadGraphImpl graph, Read r, boolean needsToBlock) throws DatabaseException { ReadEntry existing = null; synchronized(readEntryMap) { existing = (ReadEntry)readEntryMap.get(r); @@ -670,11 +670,13 @@ public class QueryCache extends QueryCacheBase { } ReadEntry entry = (ReadEntry)cache.getOrCreateReadEntry(graph, r, needsToBlock); if(entry == null) { - graph.processor.scheduleNow(new SessionTask(graph) { + graph.asyncBarrier.inc(); + graph.processor.scheduleNow(new SessionTask() { @Override public void run0(int thread) { try { runnerReadEntry(graph, r, parent, listener, procedure, needsToBlock); + graph.asyncBarrier.dec(); } catch (DatabaseException e) { Logger.defaultLogError(e); } @@ -704,7 +706,7 @@ public class QueryCache extends QueryCacheBase { } } - AsyncReadEntry getOrCreateAsyncReadEntry(ReadGraphImpl graph, AsyncRead r, boolean needsToBlock) throws DatabaseException { + AsyncReadEntry getOrCreateAsyncReadEntry(ReadGraphImpl graph, AsyncRead r, CacheEntry parent, ListenerBase listener, final AsyncProcedure procedure, boolean needsToBlock) throws DatabaseException { AsyncReadEntry existing = null; synchronized(asyncReadEntryMap) { existing = (AsyncReadEntry)asyncReadEntryMap.get(r); @@ -721,11 +723,21 @@ public class QueryCache extends QueryCacheBase { } } if(existing.isPending()) { - if(needsToBlock) - waitPending(graph, existing); - else { - return null; - } + if(needsToBlock) + waitPending(graph, existing); + else { + existing.executeWhenResultIsAvailable(graph.processor, new SessionTask(graph) { + @Override + public void run0(int thread) { + try { + runnerAsyncReadEntry(graph, r, parent, listener, procedure, needsToBlock); + } catch (DatabaseException e) { + Logger.defaultLogError(e); + } + } + }); + return null; + } } return existing; } @@ -747,19 +759,10 @@ public class QueryCache extends QueryCacheBase { } return AsyncReadEntry.computeForEach(graph, r, null, procedure, needsToBlock); } - AsyncReadEntry entry = (AsyncReadEntry)cache.getOrCreateAsyncReadEntry(graph, r, needsToBlock); + AsyncReadEntry entry = (AsyncReadEntry)cache.getOrCreateAsyncReadEntry(graph, r, parent, listener, procedure, needsToBlock); if(entry == null) { - graph.processor.scheduleNow(new SessionTask(graph) { - @Override - public void run0(int thread) { - try { - runnerAsyncReadEntry(graph, r, parent, listener, procedure, needsToBlock); - } catch (DatabaseException e) { - Logger.defaultLogError(e); - } - } - }); - return null; + // Entry was pending and this request has been queued + return null; } AsyncProcedure procedure_ = procedure != null ? procedure : emptyProcedureAsyncReadEntry; if(entry.isReady()) { diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCacheBase.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCacheBase.java index 54efda839..deeb65fdd 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCacheBase.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCacheBase.java @@ -421,7 +421,7 @@ public class QueryCacheBase { int counter = 0; while(entry.isPending()) { try { - boolean performed = graph.performPending(); + boolean performed = false;//graph.performPending(); if(!performed) { Thread.sleep(1); counter++; @@ -905,10 +905,6 @@ public class QueryCacheBase { return wrap.get(); } - public static T resultReadEntry(ReadGraphImpl graph, Read r, CacheEntry parent, ListenerBase listener, AsyncProcedure procedure) throws DatabaseException { - return (T)QueryCache.runnerReadEntry(graph, r, parent, listener, procedure, true); - } - public static T resultAsyncReadEntry(ReadGraphImpl graph, AsyncRead r, CacheEntry parent, ListenerBase listener, AsyncProcedure procedure) throws DatabaseException { return (T)QueryCache.runnerAsyncReadEntry(graph, r, parent, listener, procedure, true); } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java index 9daab950e..db9d0310f 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java @@ -14,9 +14,10 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import org.simantics.databoard.Bindings; import org.simantics.db.DevelopmentKeys; @@ -37,372 +38,483 @@ public class QueryListening { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(QueryListening.class); - final private QueryProcessor processor; - private THashSet scheduledListeners = new THashSet(); - private boolean firingListeners = false; - final THashMap> listeners = new THashMap>(10, 0.75f); - private BlockingQueue tasks = new ArrayBlockingQueue(2048); - private Map addedEntries = new HashMap<>(); - - QueryListening(QueryProcessor processor) { - this.processor = processor; - new DependencyManagementThread(processor, tasks).start(); - } - - public void sync() { - Semaphore s = new Semaphore(0); - try { - tasks.put(() -> { - s.release(); - }); - s.acquire(); - } catch (Throwable t) { - LOGGER.error("Error while waiting for query dependency management", t); - } - } - - static class DependencyManagementThread extends Thread { - - final private QueryProcessor processor; - final BlockingQueue tasks; - - DependencyManagementThread(QueryProcessor processor, BlockingQueue tasks) { - setName("Query Dependency Manager"); - this.processor = processor; - this.tasks = tasks; - } - - @Override - public void run() { - while(processor.isAlive()) { - try { - Runnable r = tasks.take(); - r.run(); - } catch (Throwable t) { - // Spurious problems? - LOGGER.error("Error while waiting for query dependency management tasks", t); - } - } - } - - } - - public boolean hasScheduledUpdates() { - return !scheduledListeners.isEmpty(); - } - - void registerDependencies(ReadGraphImpl graph, CacheEntry child, CacheEntry parent, ListenerBase listener, Object procedure, boolean inferred) { - - try { - tasks.put(() -> { - - if (parent != null && !inferred) { - try { - if(!child.isImmutable(graph)) - child.addParent(parent); - } catch (DatabaseException e) { - LOGGER.error("Error while registering query dependencies", e); - } - if (Development.DEVELOPMENT) { - if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_DEPENDENCIES, Bindings.BOOLEAN)) { - System.out.println(child + " -> " + parent); - } - } - } - - if (listener != null) - registerListener(child, listener, procedure); - - }); - } catch (InterruptedException e) { - LOGGER.error("Error while registering dependencies", e); - } - - } - - void registerFirstKnown(ListenerBase base, Object result) { - - tasks.offer(() -> { - - ListenerEntry entry = addedEntries.get(base); - if(entry != null) entry.setLastKnown(result); - - }); - - } - - public ListenerEntry registerListener(final CacheEntry entry, final ListenerBase base, final Object procedure) { - - assert (entry != null); - - if (base.isDisposed()) - return null; - - return addListener(entry, base, procedure); - - } - - /* - * Registers a listener and returns an entry iff the entry was added - */ - private ListenerEntry addListener(CacheEntry entry, ListenerBase base, Object procedure) { - - assert (entry != null); - assert (procedure != null); - - ArrayList list = listeners.get(entry); - if (list == null) { - list = new ArrayList<>(1); - listeners.put(entry, list); - } - - ListenerEntry result = new ListenerEntry(entry, base, procedure); - // Equals is here based on base - int currentIndex = list.indexOf(result); - // There was already a listener - if(currentIndex > -1) { - ListenerEntry current = list.get(currentIndex); - if(!current.base.isDisposed()) return null; - list.set(currentIndex, result); - } else { - list.add(result); - } - - if (Development.DEVELOPMENT) { - if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { - new Exception().printStackTrace(); - System.err.println("addListener -> " + list.size() + " " + entry + " " + base + " " + procedure); - } - } - - addedEntries.put(base, result); - - return result; - - } - - void scheduleListener(ListenerEntry entry) { - assert (entry != null); - if (Development.DEVELOPMENT) { - if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { - System.err.println("Scheduled " + entry.procedure); - } - } - scheduledListeners.add(entry); - } - - private void removeListener(ListenerEntry entry) { - assert (entry != null); - ArrayList list = listeners.get(entry.entry); - if(list == null) return; - boolean success = list.remove(entry); - assert (success); - if (list.isEmpty()) - listeners.remove(entry.entry); - } - - boolean hasListener(CacheEntry entry) { - if(listeners.get(entry) != null) return true; - return false; - } - - boolean hasListenerAfterDisposing(CacheEntry entry) { - if(listeners.get(entry) != null) { - ArrayList entries = listeners.get(entry); - ArrayList list = null; - for (ListenerEntry e : entries) { - if (e.base.isDisposed()) { - if(list == null) list = new ArrayList(); - list.add(e); - } - } - if(list != null) { - for (ListenerEntry e : list) { - entries.remove(e); - } - } - if (entries.isEmpty()) { - listeners.remove(entry); - return false; - } - return true; - } - return false; - } - - List getListenerEntries(CacheEntry entry) { - hasListenerAfterDisposing(entry); - if(listeners.get(entry) != null) - return listeners.get(entry); - else - return Collections.emptyList(); - } - - void processListenerReport(CacheEntry entry, Map> workarea) { - - if(!workarea.containsKey(entry)) { - - HashSet ls = new HashSet(); - for(ListenerEntry e : getListenerEntries(entry)) - ls.add(e.base); - - workarea.put(entry, ls); - - for(CacheEntry parent : entry.getParents(processor)) { - processListenerReport(parent, workarea); - ls.addAll(workarea.get(parent)); - } - - } - - } - - public synchronized ListenerReport getListenerReport() throws IOException { - - class ListenerReportImpl implements ListenerReport { - - Map> workarea = new HashMap>(); - - @Override - public void print(PrintStream b) { - Map hist = new HashMap(); - for(Map.Entry> e : workarea.entrySet()) { - for(ListenerBase l : e.getValue()) { - Integer i = hist.get(l); - hist.put(l, i != null ? i-1 : -1); - } - } - - for(Pair p : CollectionUtils.valueSortedEntries(hist)) { - b.print("" + -p.second + " " + p.first + "\n"); - } - - b.flush(); - } - - } - - ListenerReportImpl result = new ListenerReportImpl(); - - Collection all = processor.allCaches(new CacheCollectionResult()).toCollection(); - for(CacheEntryBase entry : all) { - hasListenerAfterDisposing(entry); - } - for(CacheEntryBase entry : all) { - processListenerReport(entry, result.workarea); - } - - return result; - - } - - public synchronized String reportListeners(File file) throws IOException { - - if (!processor.isAlive()) - return "Disposed!"; - - PrintStream b = new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); - ListenerReport report = getListenerReport(); - report.print(b); - - return "Done reporting listeners."; - - } - - public void fireListeners(WriteGraphImpl graph) { - - assert (!processor.updating); - assert (!processor.cache.collecting); - assert (!firingListeners); - - firingListeners = true; - - try { - - // Performing may cause further events to be scheduled. - while (!scheduledListeners.isEmpty()) { - - // Clone current events to make new entries possible during - // firing. - THashSet entries = scheduledListeners; - scheduledListeners = new THashSet(); + final private QueryProcessor processor; + private THashSet scheduledListeners = new THashSet(); + private boolean firingListeners = false; + final THashMap> listeners = new THashMap>(10, 0.75f); + private EventBusImpl eventBus; + private Consumer consumer; + private Map addedEntries = new HashMap<>(); + + static class TL extends ThreadLocal> { + + Map> allQueues = new HashMap<>(); + ArrayList> dispatchedQueues = new ArrayList<>(); + + @Override + protected synchronized ArrayList initialValue() { + ArrayList result = new ArrayList<>(); + allQueues.put(Thread.currentThread(), result); + return result; + } + + synchronized void sendToExecution() { + ArrayList rs = allQueues.remove(Thread.currentThread()); + dispatchedQueues.add(rs); + notify(); + } + + synchronized ArrayList> getDispatchedQueues() { + ArrayList> result = dispatchedQueues; + dispatchedQueues = new ArrayList<>(); + return result; + } + + } + + static class EventBusImpl { + + private static final int BUFFER_SIZE = 100; + + TL queues = new TL(); + Semaphore flush = null; + + public boolean isTerminated() { + return false; + } + + public Consumer newConsumer() { + return new Consumer() { + + @Override + public void accept(Runnable arg0) { + ArrayList l = queues.get(); + l.add(arg0); + if(l.size() == BUFFER_SIZE) { + queues.remove(); + queues.sendToExecution(); + } + } + }; + } + + public void shutdown() { + } + + public void start(ThreadFactory arg1) { + arg1.newThread(new Runnable() { + + @Override + public void run() { + synchronized(queues) { + while(!isTerminated()) { + try { + ArrayList> qs = queues.getDispatchedQueues(); + for(ArrayList queue : qs) { + for(Runnable r : queue) + r.run(); + } + if(flush != null) { + for(ArrayList queue : queues.allQueues.values()) { + for(Runnable r : queue) { + r.run(); + } + queue.clear(); + } + Semaphore s = flush; + flush = null; + s.release(); + } + queues.wait(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + }).start(); + } + + void flush() { + synchronized(queues) { + flush = new Semaphore(0); + queues.notify(); + } + try { + flush.acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + } + + QueryListening(QueryProcessor processor) { + this.processor = processor; + eventBus = new EventBusImpl(); + consumer = eventBus.newConsumer(); + eventBus.start(new ThreadFactory() + { + @Override + public Thread newThread(Runnable r) { + System.err.println("new Thread " + r); + return new Thread(r, "QueryDependencyManager"); + } + }); + } + + public void sync() { + try { + eventBus.flush(); + } catch (Throwable t) { + LOGGER.error("Error while waiting for query dependency management", t); + } + } + + public boolean hasScheduledUpdates() { + return !scheduledListeners.isEmpty(); + } + + static class RegisterParentRunnable implements Runnable { + + final CacheEntry parent; + final CacheEntry child; + + public RegisterParentRunnable(CacheEntry parent, CacheEntry child) { + this.parent = parent; + this.child = child; + } + + @Override + public void run() { + child.addParent(parent); + if (Development.DEVELOPMENT) { + if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_DEPENDENCIES, Bindings.BOOLEAN)) { + System.out.println(child + " -> " + parent); + } + } + } + + } + + static class RegisterListenerRunnable implements Runnable { + + final QueryListening queryListening; + final ListenerBase base; + final Object procedure; + final CacheEntry parent; + final CacheEntry entry; + + public RegisterListenerRunnable(QueryListening queryListening, ListenerBase base, Object procedure, CacheEntry parent, CacheEntry entry) { + this.queryListening = queryListening; + this.base = base;; + this.procedure = procedure; + this.parent = parent; + this.entry = entry; + } + + @Override + public void run() { + queryListening.addListener(entry, base, procedure); + } + + } + + void registerDependencies(ReadGraphImpl graph, CacheEntry child, CacheEntry parent, ListenerBase listener, Object procedure, boolean inferred) { + + if(inferred) { + assert(listener == null); + return; + } + + if(parent != null) { + try { + if(!child.isImmutable(graph)) + consumer.accept(new RegisterParentRunnable(parent, child)); + } catch (DatabaseException e) { + LOGGER.error("Error while registering query dependencies", e); + } + } + + if(listener != null) + if(!listener.isDisposed()) + consumer.accept(new RegisterListenerRunnable(this, listener, procedure, parent, child)); + + } + + void registerFirstKnown(ListenerBase base, Object result) { + + if(base == null) return; + + consumer.accept(() -> { + ListenerEntry entry = addedEntries.get(base); + if(entry != null) entry.setLastKnown(result); + }); + + } + + /* + * Registers a listener and returns an entry iff the entry was added + */ + private ListenerEntry addListener(CacheEntry entry, ListenerBase base, Object procedure) { + + assert (entry != null); + assert (procedure != null); + + ArrayList list = listeners.get(entry); + if (list == null) { + list = new ArrayList<>(1); + listeners.put(entry, list); + } + + ListenerEntry result = new ListenerEntry(entry, base, procedure); + // Equals is here based on base + int currentIndex = list.indexOf(result); + // There was already a listener + if(currentIndex > -1) { + ListenerEntry current = list.get(currentIndex); + if(!current.base.isDisposed()) return null; + list.set(currentIndex, result); + } else { + list.add(result); + } + + if (Development.DEVELOPMENT) { + if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { + new Exception().printStackTrace(); + System.err.println("addListener -> " + list.size() + " " + entry + " " + base + " " + procedure); + } + } + + addedEntries.put(base, result); + + return result; + + } + + void scheduleListener(ListenerEntry entry) { + assert (entry != null); + if (Development.DEVELOPMENT) { + if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { + System.err.println("Scheduled " + entry.procedure); + } + } + scheduledListeners.add(entry); + } + + private void removeListener(ListenerEntry entry) { + assert (entry != null); + ArrayList list = listeners.get(entry.entry); + if(list == null) return; + boolean success = list.remove(entry); + assert (success); + if (list.isEmpty()) + listeners.remove(entry.entry); + } + + boolean hasListener(CacheEntry entry) { + if(listeners.get(entry) != null) return true; + return false; + } + + boolean hasListenerAfterDisposing(CacheEntry entry) { + if(listeners.get(entry) != null) { + ArrayList entries = listeners.get(entry); + ArrayList list = null; + for (ListenerEntry e : entries) { + if (e.base.isDisposed()) { + if(list == null) list = new ArrayList(); + list.add(e); + } + } + if(list != null) { + for (ListenerEntry e : list) { + entries.remove(e); + } + } + if (entries.isEmpty()) { + listeners.remove(entry); + return false; + } + return true; + } + return false; + } + + List getListenerEntries(CacheEntry entry) { + hasListenerAfterDisposing(entry); + if(listeners.get(entry) != null) + return listeners.get(entry); + else + return Collections.emptyList(); + } + + void processListenerReport(CacheEntry entry, Map> workarea) { + + if(!workarea.containsKey(entry)) { + + HashSet ls = new HashSet(); + for(ListenerEntry e : getListenerEntries(entry)) + ls.add(e.base); + + workarea.put(entry, ls); + + for(CacheEntry parent : entry.getParents(processor)) { + processListenerReport(parent, workarea); + ls.addAll(workarea.get(parent)); + } + + } + + } + + public synchronized ListenerReport getListenerReport() throws IOException { + + class ListenerReportImpl implements ListenerReport { + + Map> workarea = new HashMap>(); + + @Override + public void print(PrintStream b) { + Map hist = new HashMap(); + for(Map.Entry> e : workarea.entrySet()) { + for(ListenerBase l : e.getValue()) { + Integer i = hist.get(l); + hist.put(l, i != null ? i-1 : -1); + } + } + + for(Pair p : CollectionUtils.valueSortedEntries(hist)) { + b.print("" + -p.second + " " + p.first + "\n"); + } + + b.flush(); + } + + } + + ListenerReportImpl result = new ListenerReportImpl(); + + Collection all = processor.allCaches(new CacheCollectionResult()).toCollection(); + for(CacheEntryBase entry : all) { + hasListenerAfterDisposing(entry); + } + for(CacheEntryBase entry : all) { + processListenerReport(entry, result.workarea); + } + + return result; + + } + + public synchronized String reportListeners(File file) throws IOException { + + if (!processor.isAlive()) + return "Disposed!"; + + PrintStream b = new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); + ListenerReport report = getListenerReport(); + report.print(b); + + return "Done reporting listeners."; + + } + + public void fireListeners(WriteGraphImpl graph) { + + assert (!processor.updating); + assert (!processor.cache.collecting); + assert (!firingListeners); + + firingListeners = true; + + try { - ArrayList schedule = new ArrayList(); + // Performing may cause further events to be scheduled. + while (!scheduledListeners.isEmpty()) { - for (ListenerEntry listenerEntry : entries) { + // Clone current events to make new entries possible during + // firing. + THashSet entries = scheduledListeners; + scheduledListeners = new THashSet(); - if (pruneListener(listenerEntry)) { - if (Development.DEVELOPMENT) { - if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { - new Exception().printStackTrace(); - System.err.println("Pruned " + listenerEntry.procedure); - } - } - continue; - } + ArrayList schedule = new ArrayList(); - final CacheEntry entry = listenerEntry.entry; - assert (entry != null); + for (ListenerEntry listenerEntry : entries) { - Object newValue = processor.compareTo(graph, entry, listenerEntry.getLastKnown()); + if (pruneListener(listenerEntry)) { + if (Development.DEVELOPMENT) { + if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { + new Exception().printStackTrace(); + System.err.println("Pruned " + listenerEntry.procedure); + } + } + continue; + } + + final CacheEntry entry = listenerEntry.entry; + assert (entry != null); + + Object newValue = processor.compareTo(graph, entry, listenerEntry.getLastKnown()); + + if (newValue != ListenerEntry.NOT_CHANGED) { + if (Development.DEVELOPMENT) { + if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { + new Exception().printStackTrace(); + System.err.println("Add to schedule " + listenerEntry.procedure + " with " + newValue); + } + } + schedule.add(listenerEntry); + listenerEntry.setLastKnown(entry.getResult()); + } + + } + + for(ListenerEntry listenerEntry : schedule) { + final CacheEntry entry = listenerEntry.entry; + if (Development.DEVELOPMENT) { + if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { + System.err.println("Firing " + listenerEntry.procedure); + } + } + try { + if (Development.DEVELOPMENT) { + if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { + System.err.println("Firing " + listenerEntry.procedure + " for " + listenerEntry.entry); + } + } + entry.performFromCache(graph, listenerEntry.procedure); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + } + + } finally { + firingListeners = false; + } + + } + + void updateParents(int indent, CacheEntry entry, LinkedList todo) { + + Iterable oldParents = entry.getParents(processor); + for (CacheEntry parent : oldParents) { + if(!parent.isDiscarded()) + todo.push(new UpdateEntry(entry, parent, indent + 2)); + } + + } + + private boolean pruneListener(ListenerEntry entry) { + if (entry.base.isDisposed()) { + removeListener(entry); + return true; + } else { + return false; + } + } - if (newValue != ListenerEntry.NOT_CHANGED) { - if (Development.DEVELOPMENT) { - if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { - new Exception().printStackTrace(); - System.err.println("Add to schedule " + listenerEntry.procedure + " with " + newValue); - } - } - schedule.add(listenerEntry); - listenerEntry.setLastKnown(entry.getResult()); - } - - } - - for(ListenerEntry listenerEntry : schedule) { - final CacheEntry entry = listenerEntry.entry; - if (Development.DEVELOPMENT) { - if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { - System.err.println("Firing " + listenerEntry.procedure); - } - } - try { - if (Development.DEVELOPMENT) { - if(Development.getProperty(DevelopmentKeys.QUERYPROCESSOR_LISTENERS, Bindings.BOOLEAN)) { - System.err.println("Firing " + listenerEntry.procedure + " for " + listenerEntry.entry); - } - } - entry.performFromCache(graph, listenerEntry.procedure); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - } - - } finally { - firingListeners = false; - } - - } - - void updateParents(int indent, CacheEntry entry, LinkedList todo) { - - Iterable oldParents = entry.getParents(processor); - for (CacheEntry parent : oldParents) { - if(!parent.isDiscarded()) - todo.push(new UpdateEntry(entry, parent, indent + 2)); - } - - } - - private boolean pruneListener(ListenerEntry entry) { - if (entry.base.isDisposed()) { - removeListener(entry); - return true; - } else { - return false; - } - } - } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryProcessor.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryProcessor.java index ee4b11175..50a9a827a 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryProcessor.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryProcessor.java @@ -143,6 +143,8 @@ final public class QueryProcessor extends AbstractDisposable implements ReadGrap QueryThread[] executors; public LinkedList freeScheduling = new LinkedList(); + + public LinkedList topLevelTasks = new LinkedList(); enum ThreadState { @@ -159,13 +161,12 @@ final public class QueryProcessor extends AbstractDisposable implements ReadGrap public void close() { } - public SessionTask getSubTask(ReadGraphImpl impl) { + public SessionTask getSubTask(ReadGraphImpl parent) { synchronized(querySupportLock) { int index = 0; while(index < freeScheduling.size()) { SessionTask task = freeScheduling.get(index); - if(task.hasCommonParent(task.graph, impl) && task.maybeReady()) { - queueLength.decrementAndGet(); + if(task.isSubtask(parent) && task.maybeReady()) { return freeScheduling.remove(index); } index++; @@ -178,82 +179,46 @@ final public class QueryProcessor extends AbstractDisposable implements ReadGrap * We are running errands while waiting for requests to complete. * We can only run work that is part of the current root request to avoid any deadlocks */ - public boolean performPending(ReadGraphImpl graph) { - SessionTask task = getSubTask(graph); + public boolean performPending(ReadGraphImpl under) { + SessionTask task = getSubTask(under); if(task != null) { - task.run(QueryProcessor.thread.get()); + task.run(thread.get()); return true; } return false; } - + final public void scheduleNow(SessionTask request) { - schedule(request, false); + SessionTask toExecute = scheduleOrReturnForExecution(request); + if(toExecute != null) + toExecute.run(thread.get()); } - final public void scheduleLater(SessionTask request) { - schedule(request, true); - } + final public SessionTask scheduleOrReturnForExecution(SessionTask request) { - AtomicInteger queueLength = new AtomicInteger(0); - - final public void schedule(SessionTask request, boolean late) { - - int queueLengthEstimate = queueLength.get(); - if(!late && queueLengthEstimate > 80) { - request.run(thread.get()); - return; - } - - assert(request != null); - - synchronized(querySupportLock) { + assert(request != null); - if(BarrierTracing.BOOKKEEPING) { - Exception current = new Exception(); - Exception previous = BarrierTracing.tasks.put(request, current); - if(previous != null) { - previous.printStackTrace(); - current.printStackTrace(); - } - } + synchronized(querySupportLock) { - if(late) { - int pos = request.position - 1; - if(pos < freeScheduling.size()) { - freeScheduling.add(pos, request); - queueLength.incrementAndGet(); - requests.release(); - } else { - freeScheduling.addLast(request); - queueLength.incrementAndGet(); - requests.release(); - } - } - else { - if(request.getLevel() < 4) { - if(freeScheduling.size() < 100) { - freeScheduling.addFirst(request); - queueLength.incrementAndGet(); - requests.release(); - } else { - request.run(thread.get()); - } - } else { - if(freeScheduling.size() < 20) { - freeScheduling.addFirst(request); - queueLength.incrementAndGet(); - requests.release(); - } else { - request.run(thread.get()); - } - } - } + LinkedList queue = request.rootGraph != null ? freeScheduling : topLevelTasks; + + if(BarrierTracing.BOOKKEEPING) { + Exception current = new Exception(); + Exception previous = BarrierTracing.tasks.put(request, current); + if(previous != null) { + previous.printStackTrace(); + current.printStackTrace(); + } + } + queue.addFirst(request); + requests.release(); - } + } - } + return null; + + } final int THREADS; @@ -263,20 +228,22 @@ final public class QueryProcessor extends AbstractDisposable implements ReadGrap public static abstract class SessionTask { - public final ReadGraphImpl graph; + final protected ReadGraphImpl rootGraph; private int counter = 0; protected int position = 1; private Exception trace; - public SessionTask(ReadGraphImpl graph) { - this.graph = graph; - if(graph != null) graph.asyncBarrier.inc(); + public SessionTask() { + this(null); } - - public static boolean hasCommonParent(ReadGraphImpl r1, ReadGraphImpl r2) { - if(r1 == null || r2 == null) return false; - return r1.getTopLevelGraph() == r2.getTopLevelGraph(); - } + + public SessionTask(ReadGraphImpl rootGraph) { + this.rootGraph = rootGraph; + } + + public boolean isSubtask(ReadGraphImpl graph) { + return graph.isParent(rootGraph); + } public abstract void run0(int thread); @@ -292,7 +259,7 @@ final public class QueryProcessor extends AbstractDisposable implements ReadGrap trace = new Exception(); } run0(thread); - if(graph != null) graph.asyncBarrier.dec(); + //if(graph != null && graph.asyncBarrier != null) graph.asyncBarrier.dec(); } public boolean maybeReady() { @@ -301,16 +268,16 @@ final public class QueryProcessor extends AbstractDisposable implements ReadGrap @Override public String toString() { - if(graph == null) + if(rootGraph == null) return "SessionTask[no graph]"; else - return "SessionTask[" + graph.parent + "]"; + return "SessionTask[" + rootGraph.parent + "]"; } - public int getLevel() { - if(graph == null) return 0; - else return graph.getLevel(); - } +// public int getLevel() { +// if(graph == null) return 0; +// else return graph.getLevel(); +// } } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryThread.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryThread.java index 5510944dc..cfa088a2d 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryThread.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryThread.java @@ -98,11 +98,16 @@ class QueryThread extends Thread implements SessionThread { } private boolean pumpTask() { - if(!processor.freeScheduling.isEmpty()) { - tasks.add(processor.freeScheduling.removeFirst()); - processor.queueLength.decrementAndGet(); - return true; - } + // First finish existing executions + if(!processor.freeScheduling.isEmpty()) { + tasks.add(processor.freeScheduling.removeFirst()); + return true; + } + // Check for new tasks + if(!processor.topLevelTasks.isEmpty()) { + tasks.add(processor.topLevelTasks.removeFirst()); + return true; + } return false; } @@ -226,8 +231,6 @@ class QueryThread extends Thread implements SessionThread { @Override public void run() { - processor.thread.set(index); - QuerySupport support = this.querySupport; try { @@ -242,23 +245,10 @@ class QueryThread extends Thread implements SessionThread { while(!tasks.isEmpty()) { SessionTask task = tasks.remove(tasks.size()-1); -// System.err.println("QT " + index + " runs " + task); - task.run(index); + task.run(0); } -// for(int performer=0;performer extends CacheEntryBase> implem } finally { executeGraph.asyncBarrier.dec(); - executeGraph.asyncBarrier.waitBarrier(procedure, executeGraph); + if(needsToBlock) + executeGraph.asyncBarrier.waitBarrier(procedure, executeGraph); } diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/PropertyInfo.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/PropertyInfo.java index 6ecfb8ead..286ce09aa 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/PropertyInfo.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/PropertyInfo.java @@ -30,6 +30,7 @@ import org.simantics.utils.datastructures.Pair; public class PropertyInfo { public final Resource predicate; public final String name; + public final boolean isImmutable; public final boolean isHasProperty; public final boolean isFunctional; public final Set classifications; @@ -42,9 +43,10 @@ public class PropertyInfo { public final Map> subliteralPredicates; public final ValueAccessor valueAccessor; public final boolean hasEnumerationRange; - public PropertyInfo(Resource predicate, String name, boolean isFunctional, boolean isHasProperty, Set classifications, VariableBuilder builder, Resource literalRange, Datatype requiredDatatype, String definedUnit, String requiredValueType, Binding defaultBinding, Map> subliteralPredicates, ValueAccessor valueAccessor, boolean hasEnumerationRange) { + public PropertyInfo(Resource predicate, String name, boolean isImmutable, boolean isFunctional, boolean isHasProperty, Set classifications, VariableBuilder builder, Resource literalRange, Datatype requiredDatatype, String definedUnit, String requiredValueType, Binding defaultBinding, Map> subliteralPredicates, ValueAccessor valueAccessor, boolean hasEnumerationRange) { this.predicate = predicate; this.name = name; + this.isImmutable = isImmutable; this.isFunctional = isFunctional; this.isHasProperty = isHasProperty; this.classifications = classifications; @@ -59,7 +61,6 @@ public class PropertyInfo { this.hasEnumerationRange = hasEnumerationRange; } public static PropertyInfo make(ReadGraph graph, Resource predicate, String name, boolean isFunctional, boolean isHasProperty, Set classifications, VariableBuilder builder, Resource literalRange, Datatype requiredDatatype, String definedUnit, String requiredValueType, Map> subliteralPredicates, ValueAccessor valueAccessor, boolean hasEnumerationRange) throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); if(literalRange != null) { Collection dts = graph.getAssertedObjects(literalRange, L0.HasDataType); @@ -68,11 +69,8 @@ public class PropertyInfo { if(requiredDatatype == null) requiredDatatype = dt; } } - Binding defaultBinding = requiredDatatype != null ? Bindings.getBinding(requiredDatatype) : null; - - return new PropertyInfo(predicate, name, isFunctional, isHasProperty, classifications, builder, literalRange, requiredDatatype, definedUnit, requiredValueType, defaultBinding, subliteralPredicates, valueAccessor, hasEnumerationRange); - + return new PropertyInfo(predicate, name, graph.isImmutable(predicate), isFunctional, isHasProperty, classifications, builder, literalRange, requiredDatatype, definedUnit, requiredValueType, defaultBinding, subliteralPredicates, valueAccessor, hasEnumerationRange); } public boolean hasClassification(String classification) { return classifications.contains(classification); @@ -107,20 +105,22 @@ public class PropertyInfo { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((builder == null) ? 0 : builder.hashCode()); - result = prime * result + ((classifications == null) ? 0 : classifications.hashCode()); - result = prime * result + ((defaultBinding == null) ? 0 : defaultBinding.hashCode()); - result = prime * result + ((definedUnit == null) ? 0 : definedUnit.hashCode()); - result = prime * result + (hasEnumerationRange ? 1231 : 1237); - result = prime * result + (isFunctional ? 1231 : 1237); - result = prime * result + (isHasProperty ? 1231 : 1237); - result = prime * result + ((literalRange == null) ? 0 : literalRange.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((predicate == null) ? 0 : predicate.hashCode()); - result = prime * result + ((requiredDatatype == null) ? 0 : requiredDatatype.hashCode()); - result = prime * result + ((requiredValueType == null) ? 0 : requiredValueType.hashCode()); - result = prime * result + ((subliteralPredicates == null) ? 0 : subliteralPredicates.hashCode()); - result = prime * result + ((valueAccessor == null) ? 0 : valueAccessor.hashCode()); + if(!isImmutable) { + result = prime * result + ((builder == null) ? 0 : builder.hashCode()); + result = prime * result + ((classifications == null) ? 0 : classifications.hashCode()); + result = prime * result + ((defaultBinding == null) ? 0 : defaultBinding.hashCode()); + result = prime * result + ((definedUnit == null) ? 0 : definedUnit.hashCode()); + result = prime * result + (hasEnumerationRange ? 1231 : 1237); + result = prime * result + (isFunctional ? 1231 : 1237); + result = prime * result + (isHasProperty ? 1231 : 1237); + result = prime * result + ((literalRange == null) ? 0 : literalRange.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((requiredDatatype == null) ? 0 : requiredDatatype.hashCode()); + result = prime * result + ((requiredValueType == null) ? 0 : requiredValueType.hashCode()); + result = prime * result + ((subliteralPredicates == null) ? 0 : subliteralPredicates.hashCode()); + result = prime * result + ((valueAccessor == null) ? 0 : valueAccessor.hashCode()); + } return result; } @Override @@ -132,6 +132,13 @@ public class PropertyInfo { if (getClass() != obj.getClass()) return false; PropertyInfo other = (PropertyInfo) obj; + if (predicate == null) { + if (other.predicate != null) + return false; + } else if (!predicate.equals(other.predicate)) + return false; + if(isImmutable) + return true; if (builder == null) { if (other.builder != null) return false; @@ -168,11 +175,6 @@ public class PropertyInfo { return false; } else if (!name.equals(other.name)) return false; - if (predicate == null) { - if (other.predicate != null) - return false; - } else if (!predicate.equals(other.predicate)) - return false; if (requiredDatatype == null) { if (other.requiredDatatype != null) return false; diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java index 8d364c737..1bcd91877 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java @@ -104,7 +104,7 @@ implements Read> { return DEFAULT_EXPECTED_EFFECT; } - private ExpressionEvaluator prepareEvaluator(final ReadGraph graph, final CompilationContext context, Type expectedType) throws DatabaseException { + protected ExpressionEvaluator prepareEvaluator(final ReadGraph graph, final CompilationContext context, Type expectedType) throws DatabaseException { final Variable contextVariable = new Variable("context", getContextVariableType()); LocalEnvironment localEnvironment = new AbstractLocalEnvironment() { THashMap> precalculatedVariables = new THashMap>(); @@ -156,7 +156,7 @@ implements Read> { } @SuppressWarnings("unchecked") - private Function1 eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException { + protected Function1 eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException { Object oldGraph = SCLContext.getCurrent().put("graph", graph); try { return (Function1)evaluator.eval(); diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/StandardGraphPropertyVariable.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/StandardGraphPropertyVariable.java index 7927614cd..0589f36d2 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/StandardGraphPropertyVariable.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/StandardGraphPropertyVariable.java @@ -37,7 +37,7 @@ import org.slf4j.LoggerFactory; public class StandardGraphPropertyVariable extends AbstractPropertyVariable { private static final Logger LOGGER = LoggerFactory.getLogger(StandardGraphPropertyVariable.class); - protected static final PropertyInfo NO_PROPERTY = new PropertyInfo(null, null, + protected static final PropertyInfo NO_PROPERTY = new PropertyInfo(null, null, true, false, false, Collections. emptySet(), null, null, null, null, null, null, Collections.> emptyMap(), null, false); diff --git a/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/SessionImplSocket.java b/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/SessionImplSocket.java index 8220ba743..1eb815240 100644 --- a/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/SessionImplSocket.java +++ b/bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/SessionImplSocket.java @@ -1639,7 +1639,7 @@ public abstract class SessionImplSocket implements Session, WriteRequestSchedule } else { - BlockingAsyncProcedure wrap = new BlockingAsyncProcedure(newGraph.asyncBarrier, newGraph, procedure, request) { + BlockingAsyncProcedure wrap = new BlockingAsyncProcedure(newGraph, null, procedure, request, true) { public void execute(AsyncReadGraph graph_, T result) { task.finish(); @@ -1656,6 +1656,7 @@ public abstract class SessionImplSocket implements Session, WriteRequestSchedule try { request.perform(newGraph, wrap); + wrap.dec(); wrap.get(); } catch (DatabaseException e) { @@ -3523,7 +3524,7 @@ public abstract class SessionImplSocket implements Session, WriteRequestSchedule public int getAmountOfQueryThreads() { // This must be a power of two - return 1; + return 32; // return Integer.highestOneBit(Runtime.getRuntime().availableProcessors()); } diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ConnectionRequest2.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ConnectionRequest2.java new file mode 100644 index 000000000..36a01b54c --- /dev/null +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ConnectionRequest2.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.diagram.adapter; + +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.primitiverequest.Adapter; +import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.synchronization.ErrorHandler; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.IElement; +import org.simantics.scl.runtime.tuple.Tuple3; + +/** + * @author Antti Villberg + */ +public class ConnectionRequest2 extends BaseRequest2 { + + final IDiagram diagram; + final ErrorHandler errorHandler; + + public ConnectionRequest2(ICanvasContext canvas, IDiagram diagram, Resource resource, ErrorHandler errorHandler) { + super(canvas, resource); + this.diagram = diagram; + this.errorHandler = errorHandler; + } + + @Override + public void perform(AsyncReadGraph graph, final AsyncProcedure procedure) { + + graph.forHasStatement(data, new AsyncProcedure() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + procedure.exception(graph, throwable); + } + + @Override + public void execute(AsyncReadGraph graph, Boolean result) { + + if (!result) { + procedure.execute(graph, null); + return; + } + + graph.asyncRequest(new Adapter(data, ElementFactory.class), new TransientCacheAsyncListener() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + errorHandler.error("Unexpected ElementFactory adaption failure", throwable); + procedure.execute(graph, null); + } + + @Override + public void execute(AsyncReadGraph graph, final ElementFactory factory) { + + graph.asyncRequest(new GetElementClassRequest(factory, data, canvas, diagram), new TransientCacheAsyncListener() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + errorHandler.error("Unexpected ElementClass creation failure", throwable); + procedure.execute(graph, null); + } + + @Override + public void execute(AsyncReadGraph graph, final ElementClass ec) { + + graph.asyncRequest(new SpawnRequest(canvas, ec, data), new TransientCacheAsyncListener() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + errorHandler.error("Unexpected SpawnRequest failure", throwable); + procedure.execute(graph, null); + } + + @Override + public void execute(AsyncReadGraph graph, IElement element) { + procedure.execute(graph, new Tuple3(element, ec, factory)); + } + }); + } + }); + } + }); + } + }); + } + +} diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/GraphToDiagramSynchronizer.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/GraphToDiagramSynchronizer.java index 226f47daa..37471eb3a 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/GraphToDiagramSynchronizer.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/GraphToDiagramSynchronizer.java @@ -138,6 +138,7 @@ import org.simantics.scenegraph.INode; import org.simantics.scenegraph.profile.DataNodeConstants; import org.simantics.scenegraph.profile.DataNodeMap; import org.simantics.scenegraph.profile.common.ProfileObserver; +import org.simantics.scl.runtime.tuple.Tuple3; import org.simantics.structural2.modelingRules.IModelingRules; import org.simantics.utils.datastructures.ArrayMap; import org.simantics.utils.datastructures.MapSet; @@ -1649,6 +1650,90 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID this.removedRouteGraphConnections.clear(); } + class LoadNodeListener extends DisposableListener { + + final Resource element; + public IElement lastLoaded; + + public LoadNodeListener(ListenerSupport support, Resource element) { + super(support); + this.element = element; + } + + @Override + public String toString() { + return "Node load listener for " + element; + } + + public void applyFirst(IElement loaded) { + + Object data = loaded.getHint(ElementHints.KEY_OBJECT); + + if (addedElementMap.containsKey(data)) { + // This element was just loaded, in + // which case its hints need to + // uploaded to the real mapped + // element immediately. + IElement mappedElement = getMappedElement(data); + if (DebugPolicy.DEBUG_NODE_LISTENER) + System.out.println("LOADED ADDED ELEMENT, currently mapped element: " + mappedElement); + if (mappedElement != null && (mappedElement instanceof Element)) { + if (DebugPolicy.DEBUG_NODE_LISTENER) { + System.out.println(" mapped hints: " + mappedElement.getHints()); + System.out.println(" loaded hints: " + loaded.getHints()); + } + updateMappedElement((Element) mappedElement, loaded); + } + } + + } + + @Override + public void execute(IElement loaded) { + + // Invoked when the element has been loaded. + if (DebugPolicy.DEBUG_NODE_LISTENER) + System.out.println("NODE LoadListener for " + loaded); + + if (loaded == null) { + disposeListener(); + return; + } + + + boolean first = lastLoaded == null; + + lastLoaded = loaded; + + /* + * The first invocation is postponed + */ + if(first) { + applyFirst(loaded); + return; + } + + Object data = loaded.getHint(ElementHints.KEY_OBJECT); + + // Logic for disposing listener + if (!previousContent.nodeSet.contains(data)) { + if (DebugPolicy.DEBUG_NODE_LISTENER) + System.out.println("NODE LoadListener, node not in current content: " + data + ". Disposing."); + disposeListener(); + return; + } + + // This element was already loaded. + // Just schedule an update some time + // in the future. + if (DebugPolicy.DEBUG_NODE_LISTENER) + System.out.println("PREVIOUSLY LOADED NODE UPDATED, scheduling update into the future"); + offerGraphUpdate( nodeUpdater(element, loaded) ); + + } + + } + void processNodes(AsyncReadGraph graph) throws DatabaseException { for (Map.Entry entry : changes.elements.entrySet()) { @@ -1745,6 +1830,13 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID @Override public void execute(AsyncReadGraph graph, final IElement e) { + mapElement(element, e); + synchronized (GraphToDiagramUpdater.this) { + addedElements.add(e); + addedElementMap.put(element, e); + addedConnectionMap.put(element, e); + } + // Read connection type graph.forSingleType(element, br.DIA.Connection, new Procedure() { @Override @@ -1778,87 +1870,33 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID }); } else if (content.nodeSet.contains(element)) { - Listener loadListener = new DisposableListener(canvasListenerSupport) { - - boolean firstTime = true; + graph.asyncRequest(new ReadRequest() { @Override - public String toString() { - return "Node load listener for " + element; - } - @Override - public void execute(IElement loaded) { - // Invoked when the element has been loaded. - if (DebugPolicy.DEBUG_NODE_LISTENER) - System.out.println("NODE LoadListener for " + loaded); - - if (loaded == null) { - disposeListener(); - return; - } - - if (firstTime) { - - // This is invoked before the element is actually loaded. - //System.out.println("NodeRequestProcedure " + e); - if (DebugPolicy.DEBUG_NODE_LOAD) - System.out.println("MAPPING ADDED NODE: " + element + " -> " + loaded); - mapElement(element, loaded); - synchronized (GraphToDiagramUpdater.this) { - addedElements.add(loaded); - addedElementMap.put(element, loaded); - } - - firstTime = false; - - } - - Object data = loaded.getHint(ElementHints.KEY_OBJECT); + public void run(ReadGraph graph) throws DatabaseException { - // Logic for disposing listener - if (!previousContent.nodeSet.contains(data)) { - if (DebugPolicy.DEBUG_NODE_LISTENER) - System.out.println("NODE LoadListener, node not in current content: " + data + ". Disposing."); - disposeListener(); + LoadNodeListener loadListener = new LoadNodeListener(canvasListenerSupport, element); + Tuple3 t = graph.syncRequest(new NodeRequest2(canvas, diagram, element)); + IElement e = (IElement)t.c0; + ElementClass ec = (ElementClass)t.c1; + org.simantics.diagram.adapter.ElementFactory ef = (org.simantics.diagram.adapter.ElementFactory)t.c2; + if (e == null) return; + + // This is invoked before the element is actually loaded. + //System.out.println("NodeRequestProcedure " + e); + if (DebugPolicy.DEBUG_NODE_LOAD) + System.out.println("MAPPING ADDED NODE: " + element + " -> " + e); + mapElement(element, e); + synchronized (GraphToDiagramUpdater.this) { + addedElements.add(e); + addedElementMap.put(element, e); } - if (addedElementMap.containsKey(data)) { - // This element was just loaded, in - // which case its hints need to - // uploaded to the real mapped - // element immediately. - IElement mappedElement = getMappedElement(data); - if (DebugPolicy.DEBUG_NODE_LISTENER) - System.out.println("LOADED ADDED ELEMENT, currently mapped element: " + mappedElement); - if (mappedElement != null && (mappedElement instanceof Element)) { - if (DebugPolicy.DEBUG_NODE_LISTENER) { - System.out.println(" mapped hints: " + mappedElement.getHints()); - System.out.println(" loaded hints: " + loaded.getHints()); - } - updateMappedElement((Element) mappedElement, loaded); - } - } else { - // This element was already loaded. - // Just schedule an update some time - // in the future. - if (DebugPolicy.DEBUG_NODE_LISTENER) - System.out.println("PREVIOUSLY LOADED NODE UPDATED, scheduling update into the future"); - offerGraphUpdate( nodeUpdater(element, loaded) ); - } - } - }; - - //System.out.println("NODE REQUEST: " + element); - graph.asyncRequest(new NodeRequest(canvas, diagram, element, loadListener), new AsyncProcedure() { - @Override - public void execute(AsyncReadGraph graph, IElement e) { - } - - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - error(throwable); + graph.syncRequest(new LoadRequest(canvas, diagram, ef, ec, element), loadListener); + } + }); } else { @@ -1980,8 +2018,121 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID } } } + + class LoadRouteGraphConnectionListener extends DisposableListener { + + final Resource connection; + public IElement lastLoaded; + + public LoadRouteGraphConnectionListener(ListenerSupport support, Resource connection) { + super(support); + this.connection = connection; + } + + @Override + public String toString() { + return "processRouteGraphConnections " + connection; + } + + public void applyFirst(IElement loaded) { + + Object data = loaded.getHint(ElementHints.KEY_OBJECT); + if (addedElementMap.containsKey(data)) { + // This element was just loaded, in + // which case its hints need to + // uploaded to the real mapped + // element immediately. + IElement mappedElement = getMappedElement(data); + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) + System.out.println("LOADED ADDED ROUTE GRAPH CONNECTION, currently mapped connection: " + mappedElement); + if (mappedElement instanceof Element) { + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) { + System.out.println(" mapped hints: " + mappedElement.getHints()); + System.out.println(" loaded hints: " + loaded.getHints()); + } + updateMappedElement((Element) mappedElement, loaded); + } + } + + } + + @Override + public void execute(IElement loaded) { + + // Invoked when the element has been loaded. + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) + System.out.println("ROUTE GRAPH CONNECTION LoadListener for " + loaded); + + if (loaded == null) { + disposeListener(); + return; + } + + boolean first = lastLoaded == null; + + lastLoaded = loaded; + + /* + * The first invocation is postponed + */ + if(first) { + applyFirst(loaded); + return; + } + + Object data = loaded.getHint(ElementHints.KEY_OBJECT); + + // Logic for disposing listener + if (!previousContent.routeGraphConnectionSet.contains(data)) { + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) + System.out.println("ROUTE GRAPH CONNECTION LoadListener, connection not in current content: " + data + ". Disposing."); + disposeListener(); + return; + } + + if (addedElementMap.containsKey(data)) { + // This element was just loaded, in + // which case its hints need to + // uploaded to the real mapped + // element immediately. + IElement mappedElement = getMappedElement(data); + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) + System.out.println("LOADED ADDED ROUTE GRAPH CONNECTION, currently mapped connection: " + mappedElement); + if (mappedElement instanceof Element) { + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) { + System.out.println(" mapped hints: " + mappedElement.getHints()); + System.out.println(" loaded hints: " + loaded.getHints()); + } + updateMappedElement((Element) mappedElement, loaded); + } + } else { + // This element was already loaded. + // Just schedule an update some time + // in the future. + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) + System.out.println("PREVIOUSLY LOADED ROUTE GRAPH CONNECTION UPDATED, scheduling update into the future: " + connection); + + Set dirtyNodes = new THashSet(4); + IElement mappedElement = getMappedElement(connection); + ConnectionEntity ce = mappedElement.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if (ce != null) { + for (Connection conn : ce.getTerminalConnections(null)) { + Object o = conn.node.getHint(ElementHints.KEY_OBJECT); + if (o != null) { + dirtyNodes.add(o); + if (DebugPolicy.DEBUG_CONNECTION_LISTENER) + System.out.println("Marked connectivity dirty for node: " + conn.node); + } + } + } + + offerGraphUpdate( routeGraphConnectionUpdater(connection, loaded, dirtyNodes) ); + } + } + }; - void processRouteGraphConnections(ReadGraph graph) throws DatabaseException { + + void processRouteGraphConnections(AsyncReadGraph graph) throws DatabaseException { for (Map.Entry entry : changes.routeGraphConnections.entrySet()) { final Resource connection = entry.getKey(); @@ -1992,97 +2143,39 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID if (mappedElement != null) continue; - Listener loadListener = new DisposableListener(canvasListenerSupport) { - - boolean firstTime = true; + + graph.asyncRequest(new ReadRequest() { @Override - public String toString() { - return "processRouteGraphConnections " + connection; - } - @Override - public void execute(IElement loaded) { - // Invoked when the element has been loaded. - if (DebugPolicy.DEBUG_CONNECTION_LISTENER) - System.out.println("ROUTE GRAPH CONNECTION LoadListener for " + loaded); + public void run(ReadGraph graph) throws DatabaseException { - if (loaded == null) { - disposeListener(); - return; - } - - if(firstTime) { - if (DebugPolicy.DEBUG_NODE_LOAD) - System.out.println("MAPPING ADDED ROUTE GRAPH CONNECTION: " + connection + " -> " + loaded); - mapElement(connection, loaded); - synchronized (GraphToDiagramUpdater.this) { - addedElements.add(loaded); - addedElementMap.put(connection, loaded); - addedRouteGraphConnectionMap.put(connection, loaded); - } - firstTime = false; - } + LoadRouteGraphConnectionListener loadListener = new LoadRouteGraphConnectionListener(canvasListenerSupport, connection); - Object data = loaded.getHint(ElementHints.KEY_OBJECT); + Tuple3 t = graph.syncRequest(new ConnectionRequest2(canvas, diagram, connection, errorHandler)); + IElement e = (IElement)t.c0; + ElementClass ec = (ElementClass)t.c1; + org.simantics.diagram.adapter.ElementFactory ef = (org.simantics.diagram.adapter.ElementFactory)t.c2; - // Logic for disposing listener - if (!previousContent.routeGraphConnectionSet.contains(data)) { - if (DebugPolicy.DEBUG_CONNECTION_LISTENER) - System.out.println("ROUTE GRAPH CONNECTION LoadListener, connection not in current content: " + data + ". Disposing."); - disposeListener(); + if (e == null) return; + + //System.out.println("ConnectionRequestProcedure " + e); + if (DebugPolicy.DEBUG_NODE_LOAD) + System.out.println("MAPPING ADDED ROUTE GRAPH CONNECTION: " + connection + " -> " + e); + mapElement(connection, e); + synchronized (GraphToDiagramUpdater.this) { + addedElements.add(e); + addedElementMap.put(connection, e); + addedRouteGraphConnectionMap.put(connection, e); } - if (addedElementMap.containsKey(data)) { - // This element was just loaded, in - // which case its hints need to - // uploaded to the real mapped - // element immediately. - IElement mappedElement = getMappedElement(data); - if (DebugPolicy.DEBUG_CONNECTION_LISTENER) - System.out.println("LOADED ADDED ROUTE GRAPH CONNECTION, currently mapped connection: " + mappedElement); - if (mappedElement instanceof Element) { - if (DebugPolicy.DEBUG_CONNECTION_LISTENER) { - System.out.println(" mapped hints: " + mappedElement.getHints()); - System.out.println(" loaded hints: " + loaded.getHints()); - } - updateMappedElement((Element) mappedElement, loaded); - } - } else { - // This element was already loaded. - // Just schedule an update some time - // in the future. - if (DebugPolicy.DEBUG_CONNECTION_LISTENER) - System.out.println("PREVIOUSLY LOADED ROUTE GRAPH CONNECTION UPDATED, scheduling update into the future: " + connection); - - Set dirtyNodes = new THashSet(4); - IElement mappedElement = getMappedElement(connection); - ConnectionEntity ce = mappedElement.getHint(ElementHints.KEY_CONNECTION_ENTITY); - if (ce != null) { - for (Connection conn : ce.getTerminalConnections(null)) { - Object o = conn.node.getHint(ElementHints.KEY_OBJECT); - if (o != null) { - dirtyNodes.add(o); - if (DebugPolicy.DEBUG_CONNECTION_LISTENER) - System.out.println("Marked connectivity dirty for node: " + conn.node); - } - } - } + graph.syncRequest(new LoadRequest(canvas, diagram, ef, ec, connection), loadListener); - offerGraphUpdate( routeGraphConnectionUpdater(connection, loaded, dirtyNodes) ); - } } - }; - graph.syncRequest(new ConnectionRequest(canvas, diagram, connection, errorHandler, loadListener), new Procedure() { - @Override - public void execute(final IElement e) { - } - @Override - public void exception(Throwable throwable) { - error(throwable); - } }); + + break; } case REMOVED: { @@ -2127,8 +2220,6 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID Listener loadListener = new DisposableListener(canvasListenerSupport) { - boolean firstTime = true; - @Override public String toString() { return "processBranchPoints for " + element; @@ -2144,21 +2235,6 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID return; } - if (firstTime) { - - mapElement(element, loaded); - synchronized (GraphToDiagramUpdater.this) { - addedBranchPoints.add(loaded); - addedElementMap.put(element, loaded); - ConnectionEntityImpl ce = getConnectionEntity(element); - loaded.setHint(ElementHints.KEY_CONNECTION_ENTITY, ce); - loaded.setHint(ElementHints.KEY_PARENT_ELEMENT, ce.getConnectionElement()); - } - - firstTime = false; - - } - Object data = loaded.getHint(ElementHints.KEY_OBJECT); if (addedElementMap.containsKey(data)) { // This element was just loaded, in @@ -2360,6 +2436,7 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID return "processBranchPoints"; } }); + } //System.out.println("---- PROCESS BRANCH POINTS END"); @@ -2394,9 +2471,9 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID task = Timing.BEGIN("processRouteGraphConnections"); if (!changes.routeGraphConnections.isEmpty()) { - graph.syncRequest(new ReadRequest() { + graph.syncRequest(new AsyncReadRequest() { @Override - public void run(ReadGraph graph) throws DatabaseException { + public void run(AsyncReadGraph graph) throws DatabaseException { processRouteGraphConnections(graph); } @Override @@ -2724,8 +2801,11 @@ public class GraphToDiagramSynchronizer extends AbstractDisposable implements ID diagramUpdateLock.lock(); try { - if (DebugPolicy.DEBUG_DIAGRAM_UPDATE) + if (DebugPolicy.DEBUG_DIAGRAM_UPDATE) { System.out.println("In diagramGraphUpdater:"); + System.out.println("-content = " + content); + System.out.println("-previousContent = " + previousContent); + } // Find out what has changed since the last query. Object task = Timing.BEGIN("diagramContentDifference"); diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/NodeRequest2.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/NodeRequest2.java new file mode 100644 index 000000000..61e8668da --- /dev/null +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/NodeRequest2.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.diagram.adapter; + +import java.util.List; + +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.primitiverequest.Adapter; +import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.db.procedure.Listener; +import org.simantics.diagram.synchronization.ErrorHandler; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.SubstituteElementClass; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.IElement; +import org.simantics.scl.runtime.tuple.Tuple3; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Antti Villberg + */ +public class NodeRequest2 extends BaseRequest2 { + + private static final Logger LOGGER = LoggerFactory.getLogger(NodeRequest2.class); + + final IDiagram diagram; + + public NodeRequest2(ICanvasContext canvas, IDiagram diagram, Resource resource) { + super(canvas, resource); + this.diagram = diagram; + } + + @Override + public void perform(AsyncReadGraph graph, final AsyncProcedure procedure) { + // Keep this code from crashing the whole DB client by unexpected + // throwing of NPE's somewhere in the following code that leads to + // procedure not getting called properly. + if (diagram == null) { + procedure.exception(graph, new NullPointerException("null diagram specified for resource " + data)); + return; + } + + final ErrorHandler eh = ElementFactoryUtil.getErrorHandler(diagram); + + graph.forHasStatement(data, new AsyncProcedure() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + eh.error("NodeRequest.forHasStatement failed", throwable); + procedure.execute(graph, null); + } + + @Override + public void execute(AsyncReadGraph graph, Boolean result) { + + if(!result) { + procedure.execute(graph, null); + return; + } + + graph.asyncRequest(new Adapter(data, ElementFactory.class), new TransientCacheAsyncListener() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + eh.error("NodeRequest.asyncRequest(Adapter) failed", throwable); + procedure.execute(graph, null); + } + + @Override + public void execute(AsyncReadGraph graph, final ElementFactory factory) { + + graph.asyncRequest(new GetElementClassRequest(factory, data, canvas, diagram), new TransientCacheAsyncListener() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + LOGGER.error("Unexpected error in GetElementClassRequest", throwable); + procedure.execute(graph, null); + } + + @Override + public void execute(AsyncReadGraph graph, ElementClass mutableClazz) { + List substitutes = diagram.getDiagramClass().getItemsByClass(SubstituteElementClass.class); + for (SubstituteElementClass subs : substitutes) { + mutableClazz = subs.substitute(diagram, mutableClazz); + } + final ElementClass clazz = mutableClazz; + graph.asyncRequest(new SpawnRequest(canvas, clazz, data), new TransientCacheAsyncListener() { + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + LOGGER.error("Unexpected error in SpawnRequest", throwable); + procedure.execute(graph, null); + } + + @Override + public void execute(AsyncReadGraph graph, IElement element) { + procedure.execute(graph, new Tuple3(element, clazz, factory)); + } + + }); + + } + + }); + + } + + }); + + } + + }); + + } + +} diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java index af58be479..b039b6293 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java @@ -1120,8 +1120,8 @@ public class SymbolLibraryComposite extends Composite { for(int i=0;i 0) json.append(","); Object r = res[i]; - if(r instanceof IdentifiedObject) { - Object id = ((IdentifiedObject) r).getId(); + if(r instanceof IIdentifiedObject) { + Object id = ((IIdentifiedObject) r).getId(); if(id instanceof IAdaptable) { Object resource = ((IAdaptable) id).getAdapter(Resource.class); if(resource != null) { diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java index 9a53060be..f057b98b6 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java @@ -7,16 +7,19 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; +import org.simantics.db.common.request.AsyncReadRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.procedure.AsyncProcedure; import org.simantics.document.server.JSONObject; public class DocumentRequest extends VariableRead> { - public static boolean PROFILE = false; + public static boolean PROFILE = true; // Thresholds in microseconds public static int PROFILE_THRESHOLD_NODEREQUEST = 2000; public static int PROFILE_THRESHOLD_VALUEREQUEST = 500; @@ -35,20 +38,43 @@ public class DocumentRequest extends VariableRead> { if(nodes.isEmpty()) { return Collections.emptyList(); } - - - /*TreeMap nodeMap = new TreeMap(); - - for (Variable node : nodes) { - nodeMap.put(node.getURI(graph), node); + + if(PROFILE) { + long dura = System.nanoTime()-s; + System.err.println("DocumentRequest1 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); } - System.out.println("*************************************************************************"); - for (Variable node : nodeMap.values()) { - System.out.println(" " + node.getURI(graph)); - }*/ - - for(Variable node : nodes) { - rs.add(graph.syncRequest(new NodeRequest(node), TransientCacheAsyncListener.instance())); + + graph.syncRequest(new AsyncReadRequest() { + + @Override + public void run(AsyncReadGraph graph) throws DatabaseException { + + for(Variable node : nodes) { + graph.asyncRequest(new NodeRequest(node), new AsyncProcedure () { + + @Override + public void execute(AsyncReadGraph graph, JSONObject result) { + synchronized (rs) { + rs.add(result); + } + } + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + } + + }); + + } + + } + + }); + + + if(PROFILE) { + long dura = System.nanoTime()-s; + System.err.println("DocumentRequest2 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); } ArrayList result = new ArrayList(rs); @@ -63,7 +89,7 @@ public class DocumentRequest extends VariableRead> { if(PROFILE) { long dura = System.nanoTime()-s; - System.err.println("DocumentRequest " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); + System.err.println("DocumentRequest3 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); } return result; diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodeRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodeRequest.java index 8d96c4798..82b190544 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodeRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodeRequest.java @@ -5,6 +5,8 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.variable.Variable; import org.simantics.document.server.DocumentServerUtils.AttributesRequest; +import org.simantics.utils.threads.logger.ITask; +import org.simantics.utils.threads.logger.ThreadLogger; import org.simantics.document.server.JSONObject; public class NodeRequest extends VariableRead { @@ -18,17 +20,13 @@ public class NodeRequest extends VariableRead { @Override public JSONObject perform(ReadGraph graph) throws DatabaseException { - long s = DocumentRequest.PROFILE ? System.nanoTime() : 0L; - JSONObject staticContent = graph.syncRequest(new AttributesRequest(variable)); + ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null; - if (DocumentRequest.PROFILE) { - long dura = System.nanoTime()-s; - if (dura > DocumentRequest.PROFILE_THRESHOLD_NODEREQUEST * 1e3) { - System.err.println("NodeRequest " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); - } - } + JSONObject staticContent = graph.syncRequest(new AttributesRequest(variable)); + if(DocumentRequest.PROFILE) task.finish(); + return staticContent; } diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java index f3f1177fc..99526368e 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java @@ -1,47 +1,72 @@ package org.simantics.document.server.request; -import gnu.trove.set.hash.THashSet; - import java.util.Collection; import java.util.Collections; import java.util.Set; +import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; +import org.simantics.db.common.request.AsyncReadRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.VariableChildren; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.procedure.AsyncProcedure; import org.simantics.structural.stubs.StructuralResource2; +import org.simantics.utils.threads.logger.ITask; +import org.simantics.utils.threads.logger.ThreadLogger; + +import gnu.trove.set.hash.THashSet; public class NodesRequest extends VariableRead> { public NodesRequest(Variable var) { super(var); - } - - @Override - public Set perform(ReadGraph graph) throws DatabaseException { - - long s = System.nanoTime(); - - StructuralResource2.getInstance(graph); - if(variable == null) - return Collections.emptySet(); - - Set nodes = new THashSet(); - Collection children = graph.syncRequest(new VariableChildren(variable)); - for(Variable child : children) { - Set childNodes = graph.syncRequest(new NodesRequest2(child)); - nodes.addAll(childNodes); - } - - if(DocumentRequest.PROFILE) { - long dura = System.nanoTime()-s; - System.err.println("NodesRequest " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); - } - - return nodes; - - } + } + + @Override + public Set perform(ReadGraph graph) throws DatabaseException { + + ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null; + + StructuralResource2.getInstance(graph); + if(variable == null) + return Collections.emptySet(); + + Set nodes = new THashSet(); + + Collection children = graph.syncRequest(new VariableChildren(variable)); + + graph.syncRequest(new AsyncReadRequest() { + + @Override + public void run(AsyncReadGraph graph) throws DatabaseException { + + for(Variable child : children) { + graph.asyncRequest(new NodesRequest2(child), new AsyncProcedure>() { + + @Override + public void execute(AsyncReadGraph graph, Set result) { + synchronized(nodes) { + nodes.addAll(result); + } + } + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + } + + }); + } + + } + + }); + + if(DocumentRequest.PROFILE) task.finish(); + + return nodes; + + } } \ No newline at end of file diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest2.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest2.java index 0f57a01c0..6251fb6ec 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest2.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest2.java @@ -2,48 +2,91 @@ package org.simantics.document.server.request; import gnu.trove.set.hash.THashSet; +import java.util.Collection; import java.util.Collections; import java.util.Set; import org.simantics.databoard.Bindings; +import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.common.request.AsyncReadRequest; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.request.VariableChildren; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.procedure.AsyncProcedure; import org.simantics.document.base.ontology.DocumentationResource; +import org.simantics.utils.threads.logger.ITask; +import org.simantics.utils.threads.logger.ThreadLogger; public class NodesRequest2 extends VariableRead> { public NodesRequest2(Variable var) { super(var); - } - - @Override - public Set perform(ReadGraph graph) throws DatabaseException { - - DocumentationResource DOC = DocumentationResource.getInstance(graph); - - Resource type = variable.getPossibleType(graph); - if(type == null) return Collections.emptySet(); - - if(!graph.isInheritedFrom(type, DOC.Components_Component)) return Collections.emptySet(); - - Boolean pathExists = variable.getPossiblePropertyValue(graph, DOC.Properties_pathExists, Bindings.BOOLEAN); - if(pathExists != null && !pathExists) return Collections.emptySet(); - - if(graph.isInheritedFrom(type, DOC.Components_PrimitiveComponent)) { - return Collections.singleton(variable); - } else { - Set result = new THashSet(); - for(Variable child : variable.getChildren(graph)) { - Set nodes = graph.syncRequest(new NodesRequest2(child)); - result.addAll(nodes); - } - - return result; - } - - } + } + + @Override + public Set perform(ReadGraph graph) throws DatabaseException { + + ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null; + + DocumentationResource DOC = DocumentationResource.getInstance(graph); + + Resource type = variable.getPossibleType(graph); + if(type == null) { + if(DocumentRequest.PROFILE) task.finish(); + return Collections.emptySet(); + } + + if(!graph.isInheritedFrom(type, DOC.Components_Component)) { + if(DocumentRequest.PROFILE) task.finish(); + return Collections.emptySet(); + } + + Boolean pathExists = variable.getPossiblePropertyValue(graph, DOC.Properties_pathExists, Bindings.BOOLEAN); + if(pathExists != null && !pathExists) { + if(DocumentRequest.PROFILE) task.finish(); + return Collections.emptySet(); + } + + if(graph.isInheritedFrom(type, DOC.Components_PrimitiveComponent)) { + if(DocumentRequest.PROFILE) task.finish(); + return Collections.singleton(variable); + } else { + Set result = new THashSet(); + Collection children = graph.syncRequest(new VariableChildren(variable)); + graph.syncRequest(new AsyncReadRequest() { + + @Override + public void run(AsyncReadGraph graph) throws DatabaseException { + + for(Variable child : children) { + graph.asyncRequest(new NodesRequest2(child), new AsyncProcedure>() { + + @Override + public void execute(AsyncReadGraph graph, Set vars) { + synchronized(result) { + result.addAll(vars); + } + } + + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + } + + }); + } + + } + + }); + + if(DocumentRequest.PROFILE) task.finish(); + return result; + + } + + } } \ No newline at end of file diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java index 8ca55eaa5..e117dd004 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java @@ -1,5 +1,6 @@ package org.simantics.document.server.request; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -9,15 +10,11 @@ import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.request.IndexRoot; -import org.simantics.db.common.request.PossibleTypedParent; import org.simantics.db.common.request.UnaryRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.scl.AbstractExpressionCompilationContext; -import org.simantics.db.layer0.scl.AbstractExpressionCompilationRequest; import org.simantics.db.layer0.util.RuntimeEnvironmentRequest2; import org.simantics.db.layer0.variable.Variable; -import org.simantics.document.base.ontology.DocumentationResource; -import org.simantics.document.server.request.ServerSCLHandlerValueRequest.CompilationContext; import org.simantics.layer0.Layer0; import org.simantics.scl.compiler.elaboration.expressions.EApply; import org.simantics.scl.compiler.elaboration.expressions.EConstant; @@ -30,147 +27,156 @@ import org.simantics.scl.compiler.runtime.RuntimeEnvironment; import org.simantics.scl.compiler.types.TCon; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; +import org.simantics.scl.compiler.types.exceptions.MatchException; import org.simantics.scl.compiler.types.kinds.Kinds; +import org.simantics.scl.compiler.types.util.MultiFunction; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function1; +import org.simantics.scl.runtime.function.FunctionImpl1; import org.simantics.structural2.scl.ComponentTypeProperty; import org.simantics.structural2.scl.FindPossibleComponentTypeRequest; import org.simantics.structural2.scl.ReadComponentTypeInterfaceRequest; import org.simantics.utils.datastructures.Pair; -public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationRequest { - - private final Pair componentTypeAndRoot; - private final Resource literal; - protected String possibleExpectedValueType; - - public static class CompilationContext extends AbstractExpressionCompilationContext { - public final Map propertyMap; - - public CompilationContext(RuntimeEnvironment runtimeEnvironment, - Map propertyMap) { - super(runtimeEnvironment); - this.propertyMap = propertyMap; - } - } - - private ServerSCLHandlerValueRequest(Pair componentTypeAndRoot, Resource literal, String possibleExpectedValueType) { - assert(literal != null); - this.literal = literal; - this.componentTypeAndRoot = componentTypeAndRoot; - this.possibleExpectedValueType = possibleExpectedValueType; - } - - public ServerSCLHandlerValueRequest(ReadGraph graph, Variable context) throws DatabaseException { - this(getComponentTypeAndRoot(graph, context), context.getRepresents(graph), resolveExpectedValueType(graph, context.getPredicateResource(graph))); - } - - public ServerSCLHandlerValueRequest(ReadGraph graph, Resource s, Resource o, Resource p) throws DatabaseException { - this(getComponentTypeAndRoot(graph, s, o), o, resolveExpectedValueType(graph, p)); - } - - private static Pair getComponentTypeAndRoot(ReadGraph graph, Variable property) throws DatabaseException { - Variable parent = property.getParent(graph); - Resource represents = parent.getRepresents(graph); - if(represents != null) { - Resource type = graph.syncRequest(new FindPossibleComponentTypeRequest(represents)); - if(type != null) { - Resource root = graph.syncRequest(new IndexRoot(type)); - return Pair.make(type, root); - } - } - parent = parent.getParent(graph); - Resource root = graph.syncRequest(new IndexRoot(property.getRepresents(graph))); - return Pair.make(parent.getType(graph), root); - } - - private static Pair getComponentTypeAndRoot(ReadGraph graph, Resource component, Resource literal) throws DatabaseException { - if(component != null) { - Resource type = graph.syncRequest(new FindPossibleComponentTypeRequest(component)); - if(type != null) { - Resource root = graph.syncRequest(new IndexRoot(type)); - return Pair.make(type, root); - } else { - Resource doc = graph.syncRequest(new PossibleTypedParent(component, DocumentationResource.getInstance(graph).Document)); - Resource componentType = graph.getSingleType(doc); - Resource root = graph.syncRequest(new IndexRoot(doc)); - return Pair.make(componentType, root); - } - // TODO: For Antti to consider and fix later - // Introduced to handle procedural user components where component == null - } else if (literal != null) { - Resource root = graph.syncRequest(new IndexRoot(literal)); - return Pair.make(null, root); - } else { - throw new DatabaseException("Couldn't resolve component type and root for component == null && literal == null"); - } - //throw new IllegalStateException(); - } +public class ServerSCLHandlerValueRequest extends ServerSCLValueRequestBase { + + private final Pair componentTypeAndRoot; + private final Resource literal; + protected String possibleExpectedValueType; + + public static class CompilationContext extends AbstractExpressionCompilationContext { + public final Map propertyMap; + + public CompilationContext(RuntimeEnvironment runtimeEnvironment, + Map propertyMap) { + super(runtimeEnvironment); + this.propertyMap = propertyMap; + } + } + + private ServerSCLHandlerValueRequest(Pair componentTypeAndRoot, Resource literal, String possibleExpectedValueType) { + assert(literal != null); + this.literal = literal; + this.componentTypeAndRoot = componentTypeAndRoot; + this.possibleExpectedValueType = possibleExpectedValueType; + } + + public ServerSCLHandlerValueRequest(ReadGraph graph, Variable context) throws DatabaseException { + this(getComponentTypeAndRoot(graph, context), context.getRepresents(graph), resolveExpectedValueType(graph, context.getPredicateResource(graph))); + } + + public ServerSCLHandlerValueRequest(ReadGraph graph, Resource s, Resource o, Resource p) throws DatabaseException { + this(getComponentTypeAndRoot(graph, s, o), o, resolveExpectedValueType(graph, p)); + } + + private static Pair getComponentTypeAndRoot(ReadGraph graph, Variable property) throws DatabaseException { + Variable parent = property.getParent(graph); + Resource represents = parent.getRepresents(graph); + if(represents != null) { + Resource type = graph.syncRequest(new FindPossibleComponentTypeRequest(represents)); + if(type != null) { + Resource root = graph.syncRequest(new IndexRoot(type)); + return Pair.make(type, root); + } + } + parent = parent.getParent(graph); + Resource root = graph.syncRequest(new IndexRoot(property.getRepresents(graph))); + return Pair.make(parent.getType(graph), root); + } + public static List getEffects(ReadGraph graph, Variable context) throws DatabaseException { + HandlerFn fn = (HandlerFn)compile(graph, context); + return fn.effects; + } + + public static Object compileAndEvaluate(ReadGraph graph, Variable context) throws DatabaseException { + SCLContext sclContext = SCLContext.getCurrent(); + Object oldGraph = sclContext.get("graph"); try { - ServerSCLHandlerValueRequest req = new ServerSCLHandlerValueRequest(graph, context); - return req.getExpressionEffects(graph); + Function1 exp = graph.syncRequest(new ServerSCLHandlerValueRequest(graph, context), + TransientCacheListener.instance()); + sclContext.put("graph", graph); + return exp.apply(context); } catch (DatabaseException e) { throw (DatabaseException)e; } catch (Throwable t) { throw new DatabaseException(t); + } finally { + sclContext.put("graph", oldGraph); } } - public static Object compileAndEvaluate(ReadGraph graph, Variable context) throws DatabaseException { - SCLContext sclContext = SCLContext.getCurrent(); - Object oldGraph = sclContext.get("graph"); - try { - Function1 exp = graph.syncRequest(new ServerSCLHandlerValueRequest(graph, context), - TransientCacheListener.instance()); - sclContext.put("graph", graph); - return exp.apply(context); - } catch (DatabaseException e) { - throw (DatabaseException)e; - } catch (Throwable t) { - throw new DatabaseException(t); - } finally { - sclContext.put("graph", oldGraph); - } - } - - public static Function1 compile(ReadGraph graph, Variable context) throws DatabaseException { - return graph.syncRequest(new ServerSCLHandlerValueRequest(graph, context), TransientCacheListener.instance()); - } - - public static Function1 compile(ReadGraph graph, Resource s, Resource o, Resource p) throws DatabaseException { - return graph.syncRequest(new ServerSCLHandlerValueRequest(graph, s, o, p), TransientCacheListener.>instance()); - } - - @Override - protected String getExpressionText(ReadGraph graph) - throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); - String exp = graph.getRelatedValue(literal, L0.SCLValue_expression, Bindings.STRING); - return "\\context -> " + exp; - } - - protected RuntimeEnvironmentRequest2 getRuntimeEnvironmentRequest(Resource componentType, Resource indexRoot) { - return new RuntimeEnvironmentRequest2(componentType, indexRoot) { - @Override - protected void fillEnvironmentSpecification( - EnvironmentSpecification environmentSpecification) { - } - }; - } - - @Override - protected CompilationContext getCompilationContext(ReadGraph graph) throws DatabaseException { - return graph.syncRequest(new UnaryRead,CompilationContext>(componentTypeAndRoot) { - @Override - public CompilationContext perform(ReadGraph graph) throws DatabaseException { - RuntimeEnvironment runtimeEnvironment = graph.syncRequest(getRuntimeEnvironmentRequest(parameter.first, parameter.second)); + public static Function1 compile(ReadGraph graph, Variable context) throws DatabaseException { + return graph.syncRequest(new ServerSCLHandlerValueRequest(graph, context), TransientCacheListener.instance()); + } + + public static Function1 compile(ReadGraph graph, Resource s, Resource o, Resource p) throws DatabaseException { + return graph.syncRequest(new ServerSCLHandlerValueRequest(graph, s, o, p), TransientCacheListener.instance()); + } + + public static class HandlerFn extends FunctionImpl1 { + + Function1 handler; + ArrayList effects; + + HandlerFn(Function1 handler, Type type) { + try { + this.handler = handler; + this.effects = new ArrayList(); + MultiFunction mfun = Types.matchFunction(type, 1); + mfun.effect.collectConcreteEffects(this.effects); + } catch(MatchException e) { + // Should not happen! + throw new RuntimeException(e); + } + } + + @Override + public Object apply(Object p0) { + return handler.apply(p0); + } + + } + + @Override + public Function1 perform(ReadGraph graph) throws DatabaseException { + + CompilationContext context = getCompilationContext(graph); + Type expectedType = getExpectedType(graph, context); + Function1 handler = eval(prepareEvaluator(graph, context, expectedType), graph); + return new HandlerFn(handler, expectedType); + + } + + @Override + protected String getExpressionText(ReadGraph graph) + throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + String exp = graph.getRelatedValue(literal, L0.SCLValue_expression, Bindings.STRING); + return "\\context -> " + exp; + } + + protected RuntimeEnvironmentRequest2 getRuntimeEnvironmentRequest(Resource componentType, Resource indexRoot) { + return new RuntimeEnvironmentRequest2(componentType, indexRoot) { + @Override + protected void fillEnvironmentSpecification( + EnvironmentSpecification environmentSpecification) { + } + }; + } + + @Override + protected CompilationContext getCompilationContext(ReadGraph graph) throws DatabaseException { + return graph.syncRequest(new UnaryRead,CompilationContext>(componentTypeAndRoot) { + @Override + public CompilationContext perform(ReadGraph graph) throws DatabaseException { + RuntimeEnvironment runtimeEnvironment = graph.syncRequest(getRuntimeEnvironmentRequest(parameter.first, parameter.second)); Map propertyMap; if (parameter.first != null) { propertyMap = - graph.syncRequest(new ReadComponentTypeInterfaceRequest(parameter.first, runtimeEnvironment.getEnvironment()), - TransientCacheListener.>instance()); + graph.syncRequest(new ReadComponentTypeInterfaceRequest(parameter.first, runtimeEnvironment.getEnvironment()), + TransientCacheListener.>instance()); } else { // TODO: Antti to consider // To handle procedural user components @@ -179,47 +185,47 @@ public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationR // Map propertyMap = // graph.syncRequest(new ReadComponentTypeInterfaceRequest(parameter.first, runtimeEnvironment.getEnvironment()), // TransientCacheListener.>instance()); - return new CompilationContext(runtimeEnvironment, propertyMap); - } - }); - } - - @Override - protected Type getContextVariableType() { - return VARIABLE; - } - - private static Expression accessInputVariable(Environment environment, - org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable) { - SCLValue variableParentFunction = environment.getValue(VARIABLE_PARENT); - return new EApply(new EConstant(variableParentFunction), - new EApply(new EConstant(variableParentFunction), - new EVariable(contextVariable))); - } - - protected static Expression standardGetProperty( - Environment environment, - org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable, - String name, - Type type) { - return getPropertyFlexible(environment, accessInputVariable(environment, contextVariable), name, type); - } - - @Override - protected Expression getVariableAccessExpression( - ReadGraph graph, - CompilationContext context, - org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable, - String name) throws DatabaseException { - ComponentTypeProperty property = context.propertyMap.get(name); - if(property != null) - return standardGetProperty( - context.runtimeEnvironment.getEnvironment(), - contextVariable, - name, - property.type == null ? Types.metaVar(Kinds.STAR) : property.type); - else { - + return new CompilationContext(runtimeEnvironment, propertyMap); + } + }); + } + + @Override + protected Type getContextVariableType() { + return VARIABLE; + } + + private static Expression accessInputVariable(Environment environment, + org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable) { + SCLValue variableParentFunction = environment.getValue(VARIABLE_PARENT); + return new EApply(new EConstant(variableParentFunction), + new EApply(new EConstant(variableParentFunction), + new EVariable(contextVariable))); + } + + protected static Expression standardGetProperty( + Environment environment, + org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable, + String name, + Type type) { + return getPropertyFlexible(environment, accessInputVariable(environment, contextVariable), name, type); + } + + @Override + protected Expression getVariableAccessExpression( + ReadGraph graph, + CompilationContext context, + org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable, + String name) throws DatabaseException { + ComponentTypeProperty property = context.propertyMap.get(name); + if(property != null) + return standardGetProperty( + context.runtimeEnvironment.getEnvironment(), + contextVariable, + name, + property.type == null ? Types.metaVar(Kinds.STAR) : property.type); + else { + // if(context.propertyMap.containsKey(name)) { // // org.simantics.scl.compiler.elaboration.expressions.Variable parametersVariable = new org.simantics.scl.compiler.elaboration.expressions.Variable("context", COMMAND_CONTEXT); @@ -234,45 +240,45 @@ public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationR // new ELiteral(new StringConstant(name))); // // } - - return getSpecialVariableAccessExpression(graph, context, contextVariable, name); - - } - } - - protected Expression getSpecialVariableAccessExpression(ReadGraph graph, - CompilationContext context, - org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable, - String name) throws DatabaseException { - if(name.equals("input")) { - Environment environment = context.runtimeEnvironment.getEnvironment(); - return accessInputVariable(environment, contextVariable); - } else if(name.equals("self")) - return new EVariable(contextVariable); - else - return null; - } + + return getSpecialVariableAccessExpression(graph, context, contextVariable, name); + + } + } + + protected Expression getSpecialVariableAccessExpression(ReadGraph graph, + CompilationContext context, + org.simantics.scl.compiler.elaboration.expressions.Variable contextVariable, + String name) throws DatabaseException { + if(name.equals("input")) { + Environment environment = context.runtimeEnvironment.getEnvironment(); + return accessInputVariable(environment, contextVariable); + } else if(name.equals("self")) + return new EVariable(contextVariable); + else + return null; + } @Override protected Type getExpectedType(ReadGraph graph, CompilationContext context) throws DatabaseException { return super.getExpectedType(graph, context); } - @Override - public int hashCode() { - return 31*(31*getClass().hashCode() + literal.hashCode()) + componentTypeAndRoot.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ServerSCLHandlerValueRequest other = (ServerSCLHandlerValueRequest) obj; - return literal.equals(other.literal) && componentTypeAndRoot.equals(other.componentTypeAndRoot); - } + @Override + public int hashCode() { + return 31*(31*getClass().hashCode() + literal.hashCode()) + componentTypeAndRoot.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ServerSCLHandlerValueRequest other = (ServerSCLHandlerValueRequest) obj; + return literal.equals(other.literal) && componentTypeAndRoot.equals(other.componentTypeAndRoot); + } } diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java index adff8dab5..7f9473861 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java @@ -45,7 +45,7 @@ import org.simantics.utils.datastructures.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ServerSCLValueRequest extends AbstractExpressionCompilationRequest { +public class ServerSCLValueRequest extends ServerSCLValueRequestBase { private final Pair componentTypeAndRoot; private final Resource literal; @@ -91,39 +91,9 @@ public class ServerSCLValueRequest extends AbstractExpressionCompilationRequest< return Pair.make(parent.getType(graph), root); } - private static Pair getComponentTypeAndRoot(ReadGraph graph, Resource component, Resource literal) throws DatabaseException { - if(component != null) { - Resource type = graph.syncRequest(new FindPossibleComponentTypeRequest(component)); - if(type != null) { - Resource root = graph.syncRequest(new IndexRoot(type)); - // System.err.println("getComponentTypeAndRoot3 " + graph.getPossibleURI(component) + " => " + graph.getPossibleURI(type) + " " + graph.getPossibleURI(root)); - return Pair.make(type, root); - } else { - Resource doc = graph.syncRequest(new PossibleTypedParent(component, DocumentationResource.getInstance(graph).Document)); - if(doc != null) { - Resource componentType = graph.getSingleType(doc); - Resource root = graph.syncRequest(new IndexRoot(doc)); - return Pair.make(componentType, root); - } else { - //System.err.println("component = " + component); - Resource root = graph.syncRequest(new IndexRoot(component)); -// Resource componentType = graph.getSingleType(doc); - return Pair.make(null, root); - } - } - // TODO: For Antti to consider and fix later - // Introduced to handle procedural user components where component == null - } else if (literal != null) { - Resource root = graph.syncRequest(new IndexRoot(literal)); - return Pair.make(null, root); - } else { - throw new DatabaseException("Couldn't resolve component type and root for component == null && literal == null"); - } - } - public static Object compileAndEvaluate(ReadGraph graph, Variable context) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); - Object oldGraph = sclContext.get("graph"); + Object oldGraph = sclContext.get("graph"); try { Function1 exp = compile(graph, context); sclContext.put("graph", graph); @@ -133,7 +103,7 @@ public class ServerSCLValueRequest extends AbstractExpressionCompilationRequest< } catch (Throwable t) { throw new DatabaseException(t); } finally { - sclContext.put("graph", oldGraph); + sclContext.put("graph", oldGraph); } } diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequestBase.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequestBase.java new file mode 100644 index 000000000..ab23e2ab1 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequestBase.java @@ -0,0 +1,43 @@ +package org.simantics.document.server.request; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.IndexRoot; +import org.simantics.db.common.request.PossibleTypedParent; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.scl.AbstractExpressionCompilationContext; +import org.simantics.db.layer0.scl.AbstractExpressionCompilationRequest; +import org.simantics.document.base.ontology.DocumentationResource; +import org.simantics.structural2.scl.FindPossibleComponentTypeRequest; +import org.simantics.utils.datastructures.Pair; + +abstract public class ServerSCLValueRequestBase extends AbstractExpressionCompilationRequest { + + static Pair getComponentTypeAndRoot(ReadGraph graph, Resource component, Resource literal) throws DatabaseException { + if(component != null) { + Resource type = graph.syncRequest(new FindPossibleComponentTypeRequest(component)); + if(type != null) { + Resource root = graph.syncRequest(new IndexRoot(type)); + return Pair.make(type, root); + } else { + Resource doc = graph.syncRequest(new PossibleTypedParent(component, DocumentationResource.getInstance(graph).Document)); + if(doc != null) { + Resource componentType = graph.getSingleType(doc); + Resource root = graph.syncRequest(new IndexRoot(doc)); + return Pair.make(componentType, root); + } else { + Resource root = graph.syncRequest(new IndexRoot(component)); + return Pair.make(null, root); + } + } + // TODO: For Antti to consider and fix later + // Introduced to handle procedural user components where component == null + } else if (literal != null) { + Resource root = graph.syncRequest(new IndexRoot(literal)); + return Pair.make(null, root); + } else { + throw new DatabaseException("Couldn't resolve component type and root for component == null && literal == null"); + } + } + +} diff --git a/bundles/org.simantics.issues.common/src/org/simantics/issues/common/ChildMaxIssueSeverity.java b/bundles/org.simantics.issues.common/src/org/simantics/issues/common/ChildMaxIssueSeverity.java index 1103b5f26..80f22b785 100644 --- a/bundles/org.simantics.issues.common/src/org/simantics/issues/common/ChildMaxIssueSeverity.java +++ b/bundles/org.simantics.issues.common/src/org/simantics/issues/common/ChildMaxIssueSeverity.java @@ -11,87 +11,106 @@ *******************************************************************************/ package org.simantics.issues.common; +import java.util.Collection; import java.util.Collections; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.simantics.db.AsyncReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.TernaryAsyncRead; -import org.simantics.db.procedure.AsyncMultiProcedure; +import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.AsyncProcedure; import org.simantics.issues.Severity; /** * @author Tuukka Lehtonen */ -public class ChildMaxIssueSeverity extends TernaryAsyncRead, Severity>{ +public class ChildMaxIssueSeverity extends TernaryAsyncRead, Severity> { + static class AsyncReadResult { + private AtomicReference resultRef; + private Throwable throwable; + private AtomicInteger counter = new AtomicInteger(1); + private AsyncProcedure procedure; + AsyncReadResult(AsyncProcedure procedure, AtomicReference resultRef) { + this.procedure = procedure; + this.resultRef = resultRef; + } + void except(AsyncReadGraph graph, Throwable throwable) { + this.throwable = throwable; + dec(graph); + } + void set(AsyncReadGraph graph, T result) { + resultRef.set(result); + dec(graph); + } + void inc() { + counter.incrementAndGet(); + } + void dec(AsyncReadGraph graph) { + if(counter.decrementAndGet() == 0) { + if(throwable != null) + procedure.exception(graph, throwable); + else + procedure.execute(graph, resultRef.get()); + } + } + + } + public ChildMaxIssueSeverity(Resource resource, Resource childRelation, Set typesToRecurse) { super(resource, childRelation, typesToRecurse); } -// @Override -// public Severity perform(ReadGraph graph) throws DatabaseException { -// Severity maxSeverity = null; -// //System.out.println("severityForChildren: " + NameUtils.getSafeName(graph, resource)); -// for (Resource child : graph.getObjects(resource, resource2)) { -// Severity s = graph.syncRequest(new MaxIssueSeverityRecursive(child)); -// maxSeverity = Severity.moreSevere(maxSeverity, s); -// } -// //System.out.println("severityForChildren: " + NameUtils.getSafeName(graph, resource) + " : " + maxSeverity); -// return maxSeverity; -// } - @Override public void perform(AsyncReadGraph graph, final AsyncProcedure procedure) { - //System.out.println(getClass().getSimpleName() + ": " + parameter); - graph.forTypes(parameter, new AsyncProcedure>() { - @Override - public void execute(AsyncReadGraph graph, Set result) { - if (!Collections.disjoint(parameter3, result)) { - checkChildren(graph, procedure); - } else { - procedure.execute(graph, null); - } - } - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - procedure.exception(graph, throwable); + try { + Set types = graph.getTypes(parameter); + if (!Collections.disjoint(parameter3, types)) { + checkChildren(graph, procedure); + } else { + procedure.execute(graph, null); } - }); + } catch (DatabaseException e) { + procedure.exception(graph, e); + } + } protected void checkChildren(AsyncReadGraph graph, final AsyncProcedure procedure) { - graph.forEachObject(parameter, parameter2, new AsyncMultiProcedure() { - AtomicReference maxSeverity = new AtomicReference(); - @Override - public void execute(AsyncReadGraph graph, Resource child) { + + AsyncReadResult maxSeverity = new AsyncReadResult(procedure, new AtomicReference()); + + try { + Collection children = graph.getObjects(parameter, parameter2); + for(Resource child : children) { + maxSeverity.inc(); graph.asyncRequest(new MaxIssueSeverityRecursive(child, parameter2, parameter3), new AsyncProcedure() { @Override public void execute(AsyncReadGraph graph, Severity severity) { if (severity != null) { synchronized (maxSeverity) { - maxSeverity.set(Severity.moreSevere(maxSeverity.get(), severity)); + maxSeverity.set(graph, Severity.moreSevere(maxSeverity.resultRef.get(), severity)); } + } else { + maxSeverity.dec(graph); } } @Override public void exception(AsyncReadGraph graph, Throwable throwable) { - procedure.exception(graph, throwable); + maxSeverity.except(graph, throwable); } }); } - @Override - public void finished(AsyncReadGraph graph) { - procedure.execute(graph, maxSeverity.get()); - } - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - procedure.exception(graph, throwable); - } - }); + maxSeverity.dec(graph); + } catch (DatabaseException e) { + maxSeverity.except(graph, e); + return; + } + } } diff --git a/bundles/org.simantics.issues.common/src/org/simantics/issues/common/MaxIssueSeverityRecursive.java b/bundles/org.simantics.issues.common/src/org/simantics/issues/common/MaxIssueSeverityRecursive.java index 7646271e3..ed3d6c2b9 100644 --- a/bundles/org.simantics.issues.common/src/org/simantics/issues/common/MaxIssueSeverityRecursive.java +++ b/bundles/org.simantics.issues.common/src/org/simantics/issues/common/MaxIssueSeverityRecursive.java @@ -11,91 +11,42 @@ *******************************************************************************/ package org.simantics.issues.common; +import java.util.Collection; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import org.simantics.db.AsyncReadGraph; +import org.simantics.db.ReadGraph; import org.simantics.db.Resource; -import org.simantics.db.common.request.TernaryAsyncRead; -import org.simantics.db.procedure.AsyncMultiProcedure; -import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.db.common.request.TernaryRead; +import org.simantics.db.exception.DatabaseException; import org.simantics.issues.Severity; import org.simantics.issues.ontology.IssueResource; /** * @author Tuukka Lehtonen */ -public class MaxIssueSeverityRecursive extends TernaryAsyncRead, Severity> { +public class MaxIssueSeverityRecursive extends TernaryRead, Severity> { public MaxIssueSeverityRecursive(Resource resource, Resource childRelation, Set typesToRecurse) { super(resource, childRelation, typesToRecurse); } @Override - public void perform(AsyncReadGraph graph, final AsyncProcedure procedure) { + public Severity perform(ReadGraph graph) throws DatabaseException { IssueResource ISSUE = graph.getService(IssueResource.class); - AtomicInteger issues = new AtomicInteger(); - AtomicBoolean excepted = new AtomicBoolean(false); - - graph.forEachObject(parameter, ISSUE.Issue_HasContext_Inverse, new AsyncMultiProcedure() { - @Override - public void execute(AsyncReadGraph graph, Resource result) { - issues.incrementAndGet(); - } - @Override - public void finished(AsyncReadGraph graph) { - - } - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - if(excepted.compareAndSet(false, true)) - procedure.exception(graph, throwable); - } - }); - - graph.forEachObject(parameter, ISSUE.Issue_ContextList_Element_Inverse, new AsyncMultiProcedure() { - @Override - public void execute(AsyncReadGraph graph, Resource result) { - issues.incrementAndGet(); - } - @Override - public void finished(AsyncReadGraph graph) { - - } - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - if(excepted.compareAndSet(false, true)) - procedure.exception(graph, throwable); - } - }); - - if(excepted.get()) return; - - if (issues.get() == 0) { - // This content does not have directly attached issues, try to look - // for some in the child components. - graph.asyncRequest(new ChildMaxIssueSeverity(parameter, parameter2, parameter3), procedure); - } else { - // Try local issues first - graph.asyncRequest(new MaxIssueSeveritySingle(parameter), new AsyncProcedure() { - @Override - public void execute(AsyncReadGraph graph, Severity maxSeverity) { - if (maxSeverity == null) - // No severity for local issues, try children next. - graph.asyncRequest(new ChildMaxIssueSeverity(parameter, parameter2, parameter3), procedure); - else - procedure.execute(graph, maxSeverity); - } - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - if(excepted.compareAndSet(false, true)) - procedure.exception(graph, throwable); - } - }); + Collection inv = graph.getObjects(parameter, ISSUE.Issue_HasContext_Inverse); + Collection inv2 = graph.getObjects(parameter, ISSUE.Issue_ContextList_Element_Inverse); + if(inv.isEmpty() && inv2.isEmpty()) { + return graph.syncRequest(new ChildMaxIssueSeverity(parameter, parameter2, parameter3)); } + + Severity max = graph.syncRequest(new MaxIssueSeveritySingle(parameter)); + if(max != null) + return max; + else + return graph.syncRequest(new ChildMaxIssueSeverity(parameter, parameter2, parameter3)); + } } diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/monitor/MonitorClassFactory2.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/monitor/MonitorClassFactory2.java index c7a2b6385..75d3050b9 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/monitor/MonitorClassFactory2.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/monitor/MonitorClassFactory2.java @@ -209,7 +209,7 @@ public class MonitorClassFactory2 extends SyncElementFactory { }); } - IElement mappedElement = diagram.getDiagramClass().getSingleItem(DataElementMap.class).getElement(diagram, element); + IElement mappedElement = e;//diagram.getDiagramClass().getSingleItem(DataElementMap.class).getElement(diagram, element); MonitorListener monitorListener = new MonitorListener(element, canvas, diagram, substitutions); if (mappedElement != null) { MonitorListener oldListener = mappedElement.getHint(KEY_VARIABLE_LISTENER); diff --git a/bundles/org.simantics.project/src/org/simantics/project/management/PlatformUtil.java b/bundles/org.simantics.project/src/org/simantics/project/management/PlatformUtil.java index af59dbfc1..a783acbad 100644 --- a/bundles/org.simantics.project/src/org/simantics/project/management/PlatformUtil.java +++ b/bundles/org.simantics.project/src/org/simantics/project/management/PlatformUtil.java @@ -478,8 +478,19 @@ public class PlatformUtil { } private static boolean isImmutable(Bundle bundle) { - String immutable = (String) bundle.getHeaders().get("Immutable"); - return immutable != null ? "true".equals(immutable) : true; + String immutable = (String) bundle.getHeaders().get("Immutable"); + if(immutable == null) + return true; + if("false".equals(immutable)) + return false; + if("trueWhenDeployed".equals(immutable)) { + String installHint = System.getProperty("installOntologiesAsDeployed"); + if("true".equals(installHint)) + return true; + else + return false; + } + return true; } public static class TGInfo { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java index 31ef0dc5a..23704156c 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java @@ -357,4 +357,9 @@ public class ConcreteModule implements Module { public ModuleDebugInfo getModuleDebugInfo() { return moduleDebugInfo; } + + public Map getClasses() { + return classes; + } + } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java index a6650e9a4..78dfa8f88 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java @@ -3,7 +3,9 @@ package org.simantics.scl.compiler.module.repository; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; @@ -52,6 +54,7 @@ public class ModuleRepository { private final ModuleRepository parentRepository; private final ModuleSourceRepository sourceRepository; private ConcurrentHashMap moduleCache = new ConcurrentHashMap(); + public Set runtimeEnvironments = new HashSet(); private static final ThreadLocal> PENDING_MODULES = new ThreadLocal>(); @@ -395,7 +398,9 @@ public class ModuleRepository { THashMap moduleMap = mapEntriesToModules(entries); Environment environment = createEnvironment(moduleMap, imports); THashMap runtimeModuleMap = mapEntriesToRuntimeModules(entries); - return new RuntimeEnvironmentImpl(environment, parentClassLoader, runtimeModuleMap); + RuntimeEnvironmentImpl result = new RuntimeEnvironmentImpl(environment, parentClassLoader, runtimeModuleMap); + runtimeEnvironments.add(result); + return result; } private static Environment createEnvironment( @@ -530,6 +535,10 @@ public class ModuleRepository { public void setAdvisor(ModuleCompilationOptionsAdvisor advisor) { this.advisor = advisor; } + + public Set getRuntimeEnvironments() { + return runtimeEnvironments; + } } \ No newline at end of file diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/ExpressionClassLoader.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/ExpressionClassLoader.java index 45e302720..dbfafe19b 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/ExpressionClassLoader.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/ExpressionClassLoader.java @@ -11,7 +11,7 @@ public class ExpressionClassLoader extends ClassLoader implements MutableClassLo public static final boolean TRACE_CLASS_CREATION = false; String basePackageName; - THashMap localClasses = new THashMap(); + public THashMap localClasses = new THashMap(); THashMap runtimeModuleMap; int transientPackageId = 0; THashMap valueCache = new THashMap(); @@ -57,7 +57,11 @@ public class ExpressionClassLoader extends ClassLoader implements MutableClassLo if(bytes == null) throw new ClassNotFoundException(name); - return defineClass(name, bytes, 0, bytes.length); + clazz = defineClass(name, bytes, 0, bytes.length); + resolveClass(clazz); + + return clazz; + } @Override diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java index 5ae73c412..9955c77aa 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java @@ -3,9 +3,15 @@ package org.simantics.scl.compiler.runtime; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; import org.simantics.scl.compiler.constants.Constant; import org.simantics.scl.compiler.elaboration.modules.SCLValue; import org.simantics.scl.compiler.elaboration.rules.TransformationRule; @@ -16,6 +22,7 @@ import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy; import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder; import org.simantics.scl.compiler.internal.decompilation.DecompilerFactory; import org.simantics.scl.compiler.internal.decompilation.IDecompiler; +import org.simantics.scl.compiler.module.ConcreteModule; import org.simantics.scl.compiler.module.Module; import org.simantics.scl.compiler.top.SCLCompilerConfiguration; import org.simantics.scl.compiler.top.ValueNotFound; @@ -123,7 +130,10 @@ public class RuntimeModule { } private Class getClass(String name) throws ClassNotFoundException { - //System.out.println("getClass " + name); + + Class clazz = findLoadedClass(name); + if(clazz != null) + return clazz; // If the class is not generated from SCL, use parent class loader if(!name.startsWith(SCL_PACKAGE_PREFIX)) { @@ -160,6 +170,22 @@ public class RuntimeModule { } } +// protected Class loadClass(String name, boolean resolve) +// throws ClassNotFoundException +// { +// synchronized (getClassLoadingLock(name)) { +// // First, check if the class has already been loaded +// Class c = findLoadedClass(name); +// if (c == null) { +// c = getClass(name); +// } +// if (resolve) { +// resolveClass(c); +// } +// return c; +// } +// } + @Override public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class clazz = getClass(name); @@ -168,6 +194,11 @@ public class RuntimeModule { return clazz; } + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return super.loadClass(name); + } + public Module getModule(String moduleName) { //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")"); if(moduleName.equals(this.moduleName)) @@ -285,4 +316,106 @@ public class RuntimeModule { classLoader = null; classBuilder = null; } + + public class ClassNameRecordingRemapper extends Remapper { + + private final Set classNames; + + public ClassNameRecordingRemapper(Set classNames) { + this.classNames = classNames; + } + + @Override + public String map(String typeName) { + classNames.add(typeName); + return super.map(typeName); + } + + @Override + public String mapDesc(String desc) { + //classNames.add(desc); + return super.mapDesc(desc); + } + @Override + public String mapFieldName(String owner, String name, String desc) { + return super.mapFieldName(owner, name, desc); + } + @Override + public String mapInvokeDynamicMethodName(String name, String desc) { + // TODO Auto-generated method stub + return super.mapInvokeDynamicMethodName(name, desc); + } + @Override + public String mapMethodDesc(String desc) { + //classNames.add(desc); + return super.mapMethodDesc(desc); + } + @Override + public String mapMethodName(String owner, String name, String desc) { + // TODO Auto-generated method stub + return super.mapMethodName(owner, name, desc); + } + @Override + public String mapSignature(String signature, boolean typeSignature) { + //classNames.add(signature); + return super.mapSignature(signature, typeSignature); + } + @Override + public String mapType(String type) { + classNames.add(type); + return super.mapType(type); + } + @Override + public String[] mapTypes(String[] types) { + for(String type : types) + classNames.add(type); + // TODO Auto-generated method stub + return super.mapTypes(types); + } + @Override + public Object mapValue(Object value) { + //classNames.add(value.toString()); + // TODO Auto-generated method stub + return super.mapValue(value); + } + } + + public Set classReferences(String className) { + try { + HashSet referencedClasses = new HashSet<>(); + ClassNameRecordingRemapper m = new ClassNameRecordingRemapper(referencedClasses); + ClassReader cr = new ClassReader(module.getClass(className)); + int ASM5 = 5 << 16 | 0 << 8 | 0; + //TraceClassVisitor tcv = new TraceClassVisitor(null, new PrintWriter(System.err)); + ClassVisitor cv1 = new ClassVisitor(ASM5) {}; + ClassVisitor cv = new ClassRemapper(cv1, m); + cr.accept(cv, ClassReader.SKIP_DEBUG); + return referencedClasses; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public void loadClasses() { + if(module instanceof ConcreteModule) { + ConcreteModule cm = (ConcreteModule)module; + try { + for(String className : cm.getClasses().keySet()) { + Set refs = classReferences(className); + for(String s : refs) { + String internalName = s.replace('/', '.'); + try { + classLoader.getClass(internalName); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } \ No newline at end of file diff --git a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/SCLOsgi.java b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/SCLOsgi.java index 2e717f945..35438747c 100644 --- a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/SCLOsgi.java +++ b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/SCLOsgi.java @@ -4,10 +4,14 @@ import java.util.ArrayList; import org.simantics.scl.compiler.errors.DoesNotExist; import org.simantics.scl.compiler.errors.Failable; +import org.simantics.scl.compiler.module.ConcreteModule; import org.simantics.scl.compiler.module.Module; import org.simantics.scl.compiler.module.options.ModuleCompilationOptions; import org.simantics.scl.compiler.module.options.ModuleCompilationOptionsAdvisor; import org.simantics.scl.compiler.module.repository.ModuleRepository; +import org.simantics.scl.compiler.runtime.ExpressionClassLoader; +import org.simantics.scl.compiler.runtime.RuntimeEnvironment; +import org.simantics.scl.compiler.runtime.RuntimeModule; import org.simantics.scl.compiler.source.repository.ModuleSourceRepository; import org.simantics.scl.compiler.testing.repository.TestRepository; import org.simantics.scl.osgi.internal.Activator; @@ -47,9 +51,9 @@ public class SCLOsgi { System.out.print(moduleName); System.out.print(" - "); Failable module = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName); - if(module.didSucceed()) + if(module.didSucceed()) { System.out.println("succeeded"); - else if(module == DoesNotExist.INSTANCE) + } else if(module == DoesNotExist.INSTANCE) System.out.println("does not exist"); // should not happen else { System.out.println("error"); @@ -67,4 +71,37 @@ public class SCLOsgi { } return null; } + + public static String primeClassloading() { + ArrayList modulesWithErrors = new ArrayList(); + SCLOsgi.SOURCE_REPOSITORY.forAllModules(new TObjectProcedure() { + @Override + public boolean execute(String moduleName) { + Failable module = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName); + if(module.didSucceed()) { + Failable frm = SCLOsgi.MODULE_REPOSITORY.getRuntimeModule(moduleName); + RuntimeModule rm = frm.getResult(); + rm.loadClasses(); + } else if(module == DoesNotExist.INSTANCE) + System.out.println("does not exist"); // should not happen + else { + System.out.println("error"); + modulesWithErrors.add(moduleName); + } + return true; + } + }); + for(RuntimeEnvironment env : SCLOsgi.MODULE_REPOSITORY.getRuntimeEnvironments()) { + ExpressionClassLoader ecl = (ExpressionClassLoader)env.getMutableClassLoader(); + for(String name : ecl.localClasses.keySet()) { + try { + ecl.loadClass(name.replace("/", ".")); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + } + return null; + } + } diff --git a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/procedural/CompileProceduralComponentTypeRequest.java b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/procedural/CompileProceduralComponentTypeRequest.java index 871dc1975..3a4801078 100644 --- a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/procedural/CompileProceduralComponentTypeRequest.java +++ b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/procedural/CompileProceduralComponentTypeRequest.java @@ -58,6 +58,10 @@ public class CompileProceduralComponentTypeRequest extends AbstractExpressionCom } } + public static Function1 compile(ReadGraph graph, Resource componentType) throws DatabaseException { + return graph.syncRequest(new CompileProceduralComponentTypeRequest(componentType), TransientCacheListener.instance()); + } + @Override protected String getExpressionText(ReadGraph graph) throws DatabaseException { diff --git a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogVisualizer.java b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogVisualizer.java index 5a1bb23fb..07d32a8e9 100644 --- a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogVisualizer.java +++ b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogVisualizer.java @@ -27,7 +27,7 @@ import java.util.Map; public class ThreadLogVisualizer { // Do not show tasks shorter than 5ms - final public static long DISCARD_LIMIT = 2 * 1000 * 1000; + final public static long DISCARD_LIMIT = 50 * 1000 * 1000; // 1s columns final public static long COLUMN_WIDTH = 1000000000; diff --git a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogger.java b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogger.java index 2f3425c58..67cc92903 100644 --- a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogger.java +++ b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/logger/ThreadLogger.java @@ -23,7 +23,7 @@ public class ThreadLogger implements IThreadLogger { public static String LOG_FILE = "threads.log"; - public static boolean LOG = false; + public static boolean LOG = true; static Object loggerCreationLock = new Object(); static ThreadLogger logger = null;