From ccb253462b54a0e036f0906ec8ac11a9021a968c Mon Sep 17 00:00:00 2001 From: Antti Villberg Date: Tue, 6 Oct 2020 17:05:24 +0300 Subject: [PATCH 1/1] NodeStructureRequest and NodeValueRequest fire ExternalRead twice gitlab #617 Change-Id: Id424c9254e1d80fdd26665fcbbbc9080d0878d71 --- .../layer0/variable/NodeStructureRequest.java | 321 +++++++++--------- .../db/layer0/variable/NodeValueRequest.java | 12 +- 2 files changed, 168 insertions(+), 165 deletions(-) diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java index 5d21ca56b..707fdbd0e 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java @@ -7,174 +7,177 @@ import java.util.Map; import org.simantics.databoard.util.ObjectUtils; import org.simantics.db.ReadGraph; import org.simantics.db.common.request.ParametrizedPrimitiveRead; -import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variables.NodeStructure; import org.simantics.db.procedure.Listener; import org.simantics.simulator.variable.exceptions.NodeManagerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import gnu.trove.map.hash.THashMap; @SuppressWarnings("rawtypes") class NodeStructureRequest extends ParametrizedPrimitiveRead implements VariableNodeReadRunnable { - private Listener listener = null; - private NodeStructure value = Variables.PENDING_NODE_STRUCTURE; - private boolean wasRun = false; - - static class Probe implements Runnable { - - private VariableNode node; - public NodeStructure result; - - public Probe(VariableNode node) { - this.node = node; - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - try { - result = NodeStructureRequest.get(node); - node.support.structureCache.put(node.node, result, 1000000000L); - } catch (NodeManagerException e) { - e.printStackTrace(); - } - } - - } - - public NodeStructureRequest(VariableNode node) { - super(node); - } - - @SuppressWarnings("unchecked") - @Override - public void register(ReadGraph graph, final Listener procedure) { - - if(procedure.isDisposed()) { - - // We are not listening - NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); - - if(result != null) { - // Return cached value immediately - procedure.execute(result); - } else { - NodeStructureRequest.Probe probe = new Probe(parameter); - parameter.support.manager.getRealm().asyncExec(probe); - if(probe.result != null) { - procedure.execute(probe.result); - } else { - procedure.execute(Variables.PENDING_NODE_STRUCTURE); - } - } - - return; - - } - - // We need to listen - listener = procedure; - // Register listening - parameter.support.manager.addNodeListener(parameter.node, this); - synchronized(this) { - if(wasRun) { - procedure.execute(value); - } else { - NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); - if(result != null) { - procedure.execute(result); - } else { - procedure.execute(Variables.PENDING_NODE_STRUCTURE); - } - } - } - - } - - static class NodeListener implements VariableNodeReadRunnable { - - private VariableNode node; - private NodeStructureRequest request; - - public NodeListener(VariableNode node, NodeStructureRequest request) { - this.node = node; - this.request = request; - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - node.support.manager.addNodeListener(node.node, request); - } - - } - - @SuppressWarnings("unchecked") - @Override - public void unregistered() { - parameter.support.manager.removeNodeListener(parameter.node, this); - parameter.support.structureCache.removeListening(parameter.node); - listener = null; - } - - @SuppressWarnings("unchecked") - public static NodeStructure get(VariableNode parameter) throws NodeManagerException { - List children = parameter.support.manager.getChildren(parameter.node); - List properties = parameter.support.manager.getProperties(parameter.node); - Map childMap = Collections.emptyMap(); - Map propertyMap = childMap; - if(!children.isEmpty()) { - childMap = new THashMap<>(children.size()); - for(Object o : children) { - String name = parameter.support.manager.getName(o); - childMap.put(name, o); - } - } - if(!properties.isEmpty()) { - propertyMap = new THashMap<>(properties.size()); - for(Object o : properties) { - String name = parameter.support.manager.getName(o); - propertyMap.put(name, o); - } - } - return new NodeStructure(childMap, propertyMap); - } - - @SuppressWarnings("unchecked") - @Override - public synchronized void run() { - try { - // Cache this value with infinite cache time since we are listening - NodeStructure newValue = get(parameter); - if (wasRun && ObjectUtils.objectEquals(value, newValue)) { - //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node); - return; - } - value = newValue; - parameter.support.structureCache.put(parameter.node, value); - } catch (Throwable e) { - // Must catch everything to prevent DB client from getting stuck. - Logger.defaultLogError(e); - // Invoke the exception method of the listener - Listener listener = this.listener; - if (listener != null) listener.exception(new DatabaseException("External data access error", e)); - wasRun = true; - return; - } - - // Must always invoke an existing listener, regardless of earlier errors. - Listener listener = this.listener; - if (listener != null) { - listener.execute(value); - } - wasRun = true; - } - - @Override - public String toString() { - return "NodeStructureRequest.run @ " + System.identityHashCode(this); - } + private static final Logger LOGGER = LoggerFactory.getLogger(NodeStructureRequest.class); + + private Listener listener = null; + private NodeStructure value = Variables.PENDING_NODE_STRUCTURE; + private boolean wasRun = false; + + static class Probe implements Runnable { + + private VariableNode node; + public NodeStructure result; + + public Probe(VariableNode node) { + this.node = node; + } + + @SuppressWarnings("unchecked") + @Override + public void run() { + try { + result = NodeStructureRequest.get(node); + node.support.structureCache.put(node.node, result, 1000000000L); + } catch (NodeManagerException e) { + e.printStackTrace(); + } + } + + } + + public NodeStructureRequest(VariableNode node) { + super(node); + } + + @SuppressWarnings("unchecked") + @Override + public void register(ReadGraph graph, final Listener procedure) { + + if(procedure.isDisposed()) { + + // We are not listening + NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); + + if(result != null) { + // Return cached value immediately + procedure.execute(result); + } else { + NodeStructureRequest.Probe probe = new Probe(parameter); + parameter.support.manager.getRealm().asyncExec(probe); + if(probe.result != null) { + procedure.execute(probe.result); + } else { + procedure.execute(Variables.PENDING_NODE_STRUCTURE); + } + } + + return; + + } + + // We need to listen + listener = procedure; + // Register listening + parameter.support.manager.addNodeListener(parameter.node, this); + synchronized(this) { + if(!wasRun) { + NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); + if(result != null) { + procedure.execute(result); + } else { + procedure.execute(Variables.PENDING_NODE_STRUCTURE); + } + } + } + + } + + static class NodeListener implements VariableNodeReadRunnable { + + private VariableNode node; + private NodeStructureRequest request; + + public NodeListener(VariableNode node, NodeStructureRequest request) { + this.node = node; + this.request = request; + } + + @SuppressWarnings("unchecked") + @Override + public void run() { + node.support.manager.addNodeListener(node.node, request); + } + + } + + @SuppressWarnings("unchecked") + @Override + public void unregistered() { + parameter.support.manager.removeNodeListener(parameter.node, this); + parameter.support.structureCache.removeListening(parameter.node); + listener = null; + } + + @SuppressWarnings("unchecked") + public static NodeStructure get(VariableNode parameter) throws NodeManagerException { + List children = parameter.support.manager.getChildren(parameter.node); + List properties = parameter.support.manager.getProperties(parameter.node); + Map childMap = Collections.emptyMap(); + Map propertyMap = childMap; + if(!children.isEmpty()) { + childMap = new THashMap<>(children.size()); + for(Object o : children) { + String name = parameter.support.manager.getName(o); + childMap.put(name, o); + } + } + if(!properties.isEmpty()) { + propertyMap = new THashMap<>(properties.size()); + for(Object o : properties) { + String name = parameter.support.manager.getName(o); + propertyMap.put(name, o); + } + } + return new NodeStructure(childMap, propertyMap); + } + + @SuppressWarnings("unchecked") + @Override + public synchronized void run() { + try { + // Cache this value with infinite cache time since we are listening + NodeStructure newValue = get(parameter); + if (wasRun && ObjectUtils.objectEquals(value, newValue)) { + //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node); + return; + } + value = newValue; + parameter.support.structureCache.put(parameter.node, value); + } catch (Throwable e) { + // Must catch everything to prevent DB client from getting stuck. + LOGGER.error("Error while computing node structure", e); + // Invoke the exception method of the listener + Listener listener = this.listener; + if (listener != null) { + listener.exception(new DatabaseException("External data access error", e)); + wasRun = true; + } + return; + } + + // Must always invoke an existing listener, regardless of earlier errors. + Listener listener = this.listener; + if (listener != null) { + listener.execute(value); + wasRun = true; + } + } + + @Override + public String toString() { + return "NodeStructureRequest.run @ " + System.identityHashCode(this); + } } \ No newline at end of file diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java index 140ee8fa0..b44f16b30 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java @@ -110,9 +110,7 @@ class NodeValueRequest extends ParametrizedPrimitiveRead listener = this.listener; - if (listener != null) listener.exception(new DatabaseException("External data access error", e)); - wasRun = true; + if (listener != null) { + listener.exception(new DatabaseException("External data access error", e)); + wasRun = true; + } return; } // Must always invoke an existing listener, regardless of earlier errors. @@ -228,8 +228,8 @@ class NodeValueRequest extends ParametrizedPrimitiveRead