From 82ed7c74dbd83a2a557e781b8674b3262b52da54 Mon Sep 17 00:00:00 2001 From: jsimomaa Date: Fri, 28 Apr 2017 13:07:23 +0300 Subject: [PATCH] Some fixes for resource cleaning spreadsheets in simupedia refs #7173 Change-Id: I2b8cf12a0abae25f34e691c5251a34fa0e2ab1f9 --- .../db/layer0/StandardNodeManager.java | 10 ++- .../simantics/db/layer0/StandardRealm.java | 83 +++++++++++++------ .../db/layer0/StandardSessionManager.java | 13 ++- .../scl/Document/All.scl | 12 +++ .../modeling/scl/SCLNodeManager.java | 13 ++- .../org/simantics/modeling/scl/SCLRealm.java | 74 ++++++++++++----- .../modeling/scl/SCLSessionManager.java | 8 +- .../scl/Spreadsheet/All.scl | 5 +- .../simantics/spreadsheet/graph/GraphUI.java | 2 +- .../spreadsheet/graph/SpreadsheetRealm.java | 11 ++- .../graph/SpreadsheetSessionManager.java | 72 +++++----------- .../graph/adapter/SpreadsheetBookRemover.java | 2 +- .../SpreadsheetEvaluationEnvironment.java | 5 +- 13 files changed, 197 insertions(+), 113 deletions(-) diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardNodeManager.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardNodeManager.java index 099ab025f..c48c3f9d1 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardNodeManager.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardNodeManager.java @@ -223,7 +223,7 @@ public abstract class StandardNodeManager> implements Realm { - - String id; - Thread executorThread; - ExecutorService executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(), new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - executorThread = new Thread(r); - return executorThread; - } - }); - - Semaphore beginSyncExec = new Semaphore(0); - Semaphore endSyncExec = new Semaphore(0); + + private String id; + private Thread executorThread; + private StandardRealmThreadFactory factory = new StandardRealmThreadFactory(this); + private ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS, + new LinkedBlockingQueue(), factory); + private Semaphore beginSyncExec = new Semaphore(0); + private Semaphore endSyncExec = new Semaphore(0); - Engine engine; - StandardSessionManager sessionManager; - StandardNodeManager nodeManager; + private Engine engine; + private StandardNodeManager nodeManager; - Runnable scheduleSyncExec = new Runnable() { + private Runnable scheduleSyncExec = new Runnable() { @Override public void run() { beginSyncExec.release(); @@ -44,13 +38,12 @@ abstract public class StandardRealm> i } }; - protected StandardRealm(StandardSessionManager sessionManager, Engine engine, String id) { - this.sessionManager = sessionManager; + protected StandardRealm(Engine engine, String id) { this.engine = engine; this.id = id; this.nodeManager = createManager(); } - + abstract protected StandardNodeManager createManager(); protected String getSCLContextKey() { @@ -149,16 +142,58 @@ abstract public class StandardRealm> i } public void close() { - sessionManager.removeRealm(id); executor.shutdown(); try { - executor.awaitTermination(500L, TimeUnit.MILLISECONDS); + if (!executor.awaitTermination(500L, TimeUnit.MILLISECONDS)) { + List runnablesLeft = executor.shutdownNow(); + if (!runnablesLeft.isEmpty()) { + getLogger().info("Runnables left for realm " + this + " after executor shutdown! " + runnablesLeft); + } + } } catch (InterruptedException e) { + getLogger().info("Could not shutdown executor " + executor + " for realm " + this, e); } + + factory.clear(); + factory = null; + // Should never be true + if (!executorThread.isAlive()) + executorThread.interrupt(); + executorThread = null; + executor = null; + + // Clear nodeManager + nodeManager.clear(); + nodeManager = null; } public StandardNodeManager getNodeManager() { return nodeManager; } - + + public abstract org.slf4j.Logger getLogger(); + + private void setExecutorThread(Thread t) { + executorThread = t; + } + + private static class StandardRealmThreadFactory implements ThreadFactory { + + private StandardRealm realm; + + public StandardRealmThreadFactory(StandardRealm realm) { + this.realm = realm; + } + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + realm.setExecutorThread(t); + return t; + } + + void clear() { + realm = null; + } + } } diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardSessionManager.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardSessionManager.java index b82860de1..cdd8e074c 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardSessionManager.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardSessionManager.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import org.simantics.db.ReadGraph; +import org.simantics.db.WriteGraph; import org.simantics.db.common.request.ParametrizedPrimitiveRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.NodeSupport; @@ -75,7 +76,9 @@ abstract public class StandardSessionManager removedRealm = REALMS.remove(key); + if (removedRealm != null) + removedRealm.close(); } Listener> listener = getOrDisposeListener(key); if(listener != null) { @@ -104,10 +107,14 @@ abstract public class StandardSessionManager createRealm(Engine engine, String id); - public void removeRealm(String id) { + public void removeRealm(WriteGraph graph, String id) throws DatabaseException { modifyRealms(id, null); + // remove listeners from this realm + realmListeners.remove(id); // if node support has been created remove it as well - SUPPORTS.remove(id); + NodeSupport support = SUPPORTS.remove(id); + if (support != null) + support.dispose(); } public Collection getRealms() { diff --git a/bundles/org.simantics.document.server/scl/Document/All.scl b/bundles/org.simantics.document.server/scl/Document/All.scl index fa83a224a..98a632699 100644 --- a/bundles/org.simantics.document.server/scl/Document/All.scl +++ b/bundles/org.simantics.document.server/scl/Document/All.scl @@ -150,3 +150,15 @@ consoleLog state message = do contextDocument :: CommandContext -> IDocument contextDocument ctx = justValue ctx "__document__" + +importJava "org.simantics.document.server.io.IRequest" where + @private + data IRequest + + @private + getParameter :: IRequest -> String -> Maybe String + +possibleQueryParameterFromContext :: CommandContext -> String -> Maybe String +possibleQueryParameterFromContext context parameter = do + request = fromJust $ possibleValue context "__request__" + getParameter request parameter diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java index e41dd35da..2e53ce85a 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java @@ -79,7 +79,7 @@ public class SCLNodeManager extends AbstractNodeManager { @Override public String getName(String node) { if(ROOT.equals(node)) { - String id = realm.id; + String id = realm.getId(); int lastSlash = id.lastIndexOf("/"); if(lastSlash == -1) throw new IllegalStateException("Invalid realm id " + id); String name = id.substring(lastSlash+1); @@ -226,7 +226,7 @@ public class SCLNodeManager extends AbstractNodeManager { checkThreadAccess(); valueCache.put(node, value); realm.getConnection().setVariable(node, getType(node), value); - realm.nodeManager.valueCache.put(node, value); + realm.getNodeManager().valueCache.put(node, value); refreshVariables(); } @@ -241,8 +241,8 @@ public class SCLNodeManager extends AbstractNodeManager { support.structureCache.put(ROOT, null); support.valueCache.put(node, null); - realm.nodeManager.valueCache.put(node, value); - realm.nodeManager. + realm.getNodeManager().valueCache.put(node, value); + realm.getNodeManager(). refreshVariables(); } @@ -275,4 +275,9 @@ public class SCLNodeManager extends AbstractNodeManager { public String getPropertyURI(String parent, String property) { return ModelingResources.URIs.SCLCommandSession_hasValue; } + + public void clear() { + valueCache.clear(); + listeners.clear(); + } } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLRealm.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLRealm.java index ea82e7e4c..9870ad785 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLRealm.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLRealm.java @@ -1,8 +1,8 @@ package org.simantics.modeling.scl; import java.io.IOException; +import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; @@ -19,7 +19,6 @@ import org.simantics.scl.compiler.types.Type; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function; import org.simantics.scl.runtime.tuple.Tuple0; -import org.simantics.simulator.variable.NodeManager; import org.simantics.simulator.variable.Realm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,26 +30,21 @@ public class SCLRealm implements Realm { public static final String SCL = "scl"; - THashMap contextTypes = new THashMap(); + private THashMap contextTypes = new THashMap(); - CommandSession connection; - String id; - Thread executorThread; - ExecutorService executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(), new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - executorThread = new Thread(r); - return executorThread; - } - }); + private CommandSession connection; + private String id; + private Thread executorThread; + private SCLRealmThreadFactory factory = new SCLRealmThreadFactory(this); + private ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS, + new LinkedBlockingQueue(), factory); - Semaphore beginSyncExec = new Semaphore(0); - Semaphore endSyncExec = new Semaphore(0); + private Semaphore beginSyncExec = new Semaphore(0); + private Semaphore endSyncExec = new Semaphore(0); - SCLNodeManager nodeManager; + private SCLNodeManager nodeManager; - Runnable scheduleSyncExec = new Runnable() { + private Runnable scheduleSyncExec = new Runnable() { @Override public void run() { beginSyncExec.release(); @@ -170,13 +164,30 @@ public class SCLRealm implements Realm { SCLSessionManager.CONNECTIONS.remove(id); executor.shutdown(); try { - executor.awaitTermination(500L, TimeUnit.MILLISECONDS); + if (!executor.awaitTermination(500L, TimeUnit.MILLISECONDS)) { + List runnables = executor.shutdownNow(); + if (!runnables.isEmpty()) { + LOGGER.info("Some runnables left to execute in realm " + this + ": " + runnables); + } + } } catch (InterruptedException e) { + LOGGER.info("Could not shutdown executor " + executor + " in realm " + this, e); } //connection.close(); + + factory.clear(); + factory = null; + // Should not happen + if (executorThread.isAlive()) + executorThread.interrupt(); + executorThread = null; + executor = null; + + // clear nodeManager + nodeManager.clear(); } - public NodeManager getNodeManager() { + public SCLNodeManager getNodeManager() { return nodeManager; } @@ -221,4 +232,27 @@ public class SCLRealm implements Realm { } } + private static class SCLRealmThreadFactory implements ThreadFactory { + + private SCLRealm realm; + + public SCLRealmThreadFactory(SCLRealm realm) { + this.realm = realm; + } + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + realm.setThread(t); + return t; + } + + void clear() { + realm = null; + } + } + + private void setThread(Thread t) { + this.executorThread = t; + } } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLSessionManager.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLSessionManager.java index 8fe0c7095..af036026b 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLSessionManager.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLSessionManager.java @@ -51,8 +51,12 @@ public class SCLSessionManager { } public static synchronized void removeRealm(String id) { - CONNECTIONS.remove(id); + SCLRealm realm = CONNECTIONS.remove(id); + if (realm != null) + realm.close(); // if node support has been created remove it as well - SUPPORTS.remove(id); + NodeSupport support = SUPPORTS.remove(id); + if (support != null) + support.dispose(); } } diff --git a/bundles/org.simantics.spreadsheet.graph/scl/Spreadsheet/All.scl b/bundles/org.simantics.spreadsheet.graph/scl/Spreadsheet/All.scl index 532331e55..88d8e47aa 100644 --- a/bundles/org.simantics.spreadsheet.graph/scl/Spreadsheet/All.scl +++ b/bundles/org.simantics.spreadsheet.graph/scl/Spreadsheet/All.scl @@ -56,7 +56,10 @@ importJava "org.simantics.spreadsheet.util.SpreadsheetUtils" where selectColumn :: Integer -> TableCell -> Boolean setSCLLine :: Resource -> Integer -> String -> () sheetRun :: Resource -> Variable -> Variable - + +importJava "org.simantics.spreadsheet.graph.SpreadsheetSessionManager" where + removeSpreadsheetSession :: Variable -> () + importJava "org.simantics.spreadsheet.graph.ExcelImport" where importBook :: Resource -> File -> () diff --git a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/GraphUI.java b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/GraphUI.java index ddfe45688..5850eb847 100644 --- a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/GraphUI.java +++ b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/GraphUI.java @@ -603,7 +603,7 @@ public class GraphUI implements Adaptable, ListenerSupport, AsyncListenerSupport String contextURI = context.getURI(graph); String sessionName = context.getParent(graph).getURI(graph); - SpreadsheetSessionManager.getInstance().removeRealm(sessionName); + SpreadsheetSessionManager.getInstance().removeRealm(graph, sessionName); SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName); } diff --git a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetRealm.java b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetRealm.java index 0780410e2..90ef89fab 100644 --- a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetRealm.java +++ b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetRealm.java @@ -2,16 +2,25 @@ package org.simantics.spreadsheet.graph; import org.simantics.db.layer0.StandardNodeManager; import org.simantics.db.layer0.StandardRealm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SpreadsheetRealm extends StandardRealm { + private static final Logger LOGGER = LoggerFactory.getLogger(SpreadsheetRealm.class); + SpreadsheetRealm(SpreadsheetBook book, String id) { - super(SpreadsheetSessionManager.getInstance(), book, id); + super(book, id); } @Override protected StandardNodeManager createManager() { return new SpreadsheetNodeManager(this); } + + @Override + public Logger getLogger() { + return LOGGER; + } } diff --git a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetSessionManager.java b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetSessionManager.java index 659eddf74..d90fe3106 100644 --- a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetSessionManager.java +++ b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetSessionManager.java @@ -13,18 +13,24 @@ import java.util.Set; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.StandardRealm; import org.simantics.db.layer0.StandardSessionManager; import org.simantics.db.layer0.variable.ProxyVariables; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; +import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment; import org.simantics.spreadsheet.graph.synchronization.SpreadsheetSynchronizationEventHandler; import org.simantics.spreadsheet.resource.SpreadsheetResource; import org.simantics.structural.synchronization.Synchronizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SpreadsheetSessionManager extends StandardSessionManager { + private static final Logger LOGGER = LoggerFactory.getLogger(SpreadsheetSessionManager.class); + private static SpreadsheetSessionManager INSTANCE; public static SpreadsheetSessionManager getInstance() { @@ -148,56 +154,18 @@ public class SpreadsheetSessionManager extends StandardSessionManager createRealm(SpreadsheetBook engine, String id) { return new SpreadsheetRealm(engine, id); } - -// static ConcurrentHashMap REALMS = -// new ConcurrentHashMap(); -// -// static ConcurrentHashMap> SUPPORTS = -// new ConcurrentHashMap>(); -// -// public static SpreadsheetRealm sclRealmById(String id) { -// // CONNECTIONS is ConcurrentHashMap so no synchronization is needed here -// return REALMS.get(id); -// } -// -// public static NodeSupport getOrCreateNodeSupport(String id) { -// synchronized(SUPPORTS) { -// NodeSupport result = SUPPORTS.get(id); -// if(result == null) { -// SpreadsheetRealm realm = getOrCreateSCLRealm(id); -// result = new NodeSupport(realm.getNodeManager()); -// SUPPORTS.put(id, result); -// } -// return result; -// } -// } -// -// public static SpreadsheetRealm createRealm() { -// synchronized(REALMS) { -// String id = UUID.randomUUID().toString(); -// return createRealm(id); -// } -// } -// -// public static SpreadsheetRealm getOrCreateSCLRealm(String id) { -// synchronized(REALMS) { -// SpreadsheetRealm session = sclRealmById(id); -// if(session == null) -// return createRealm(id); -// else -// return session; -// } -// } -// -// private static SpreadsheetRealm createRealm(String id) { -// SpreadsheetBook book = new SpreadsheetBook(); -// SpreadsheetRealm realm = new SpreadsheetRealm(book, id); -// REALMS.put(id, realm); -// return realm; -// } -// -// public static void removeRealm(String id) { -// REALMS.remove(id); -// } - + + @Override + public void removeRealm(WriteGraph graph, String id) throws DatabaseException { + StandardRealm realm = getOrCreateRealm(graph, id); + SpreadsheetEvaluationEnvironment.removeInstance(realm.getEngine()); + super.removeRealm(graph, id); + } + + // Utility function for SCL, this should maybe be replaced with something better in the future + public static void removeSpreadsheetSession(WriteGraph graph, Variable runVariable) throws DatabaseException { + String uri = runVariable.getParent(graph).getURI(graph); + getInstance().removeRealm(graph, uri); + } } + diff --git a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/adapter/SpreadsheetBookRemover.java b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/adapter/SpreadsheetBookRemover.java index 63b1bd4d2..138829949 100644 --- a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/adapter/SpreadsheetBookRemover.java +++ b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/adapter/SpreadsheetBookRemover.java @@ -27,7 +27,7 @@ public class SpreadsheetBookRemover extends EntityRemover { if (bookURI != null) { for (String realmId : SpreadsheetSessionManager.getInstance().getRealms()) { if (realmId.startsWith(bookURI)) { - SpreadsheetSessionManager.getInstance().removeRealm(realmId); + SpreadsheetSessionManager.getInstance().removeRealm(graph, realmId); } } } diff --git a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/formula/SpreadsheetEvaluationEnvironment.java b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/formula/SpreadsheetEvaluationEnvironment.java index 2747a337b..08483bccb 100644 --- a/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/formula/SpreadsheetEvaluationEnvironment.java +++ b/bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/formula/SpreadsheetEvaluationEnvironment.java @@ -44,7 +44,7 @@ public class SpreadsheetEvaluationEnvironment { return book; } - public static Map INSTANCES = new HashMap<>(); + private static Map INSTANCES = new HashMap<>(); public static SpreadsheetEvaluationEnvironment getInstance(SpreadsheetBook book) { SpreadsheetEvaluationEnvironment env = INSTANCES.get(book); @@ -55,4 +55,7 @@ public class SpreadsheetEvaluationEnvironment { return env; } + public static boolean removeInstance(SpreadsheetBook book) { + return INSTANCES.remove(book) != null; + } } -- 2.43.2