From c379aba692b426ae5c9efe862cd27da03e9e5347 Mon Sep 17 00:00:00 2001 From: AlexisDrogoul Date: Thu, 21 Mar 2024 08:49:13 +0700 Subject: [PATCH] Commit on the runtime of simulations and synchronizations with displays - all the management done with ExecutorService (for simulations) and volatile boolean variables (for the rendition of displays) is replaced by (simpler) semaphores; - The acquisition of the semaphores now occur at more relevant places (allowing to synchronize also the 2D/3D renderer threads for a more accurate synchronization -- see #124). - Each simulation now keeps its own thread throughout the experiment: easier for debugging, and also makes it possible to define ThreadLocal variables in shared objects (in skills, for instance) that will remain attached to the simulation *** LOTS OF TESTS ARE NECESSARY TO ENSURE THAT IT CAN BE A GOOD FOUNDATION FOR THE GAMA RUNTIME *** Known issues: - no time out is set (yet) for the acquisition of the semaphore, which can potentially lead to infinite loop/wait when the rendition is not done properly - the max. number of threads to use for the execution of simulations is not (yet) respected - errors are visible in the console when exiting GAMA and a simulation is running. - the release of signals from the semaphores is not fair (yet). - the problem where a chart in 2d slows down all the other displays when the mouse or keyboard events are targeted to it is not (yet) solved. --- .../common/interfaces/IDisplaySurface.java | 31 +-- .../core/kernel/experiment/BatchAgent.java | 4 +- .../simulation/SimulationPopulation.java | 15 +- .../src/gama/core/outputs/AbstractOutput.java | 18 +- .../core/outputs/AbstractOutputManager.java | 35 +-- .../outputs/AbstractValuedDisplayOutput.java | 3 - gama.core/src/gama/core/outputs/IOutput.java | 54 +++-- .../core/outputs/LayeredDisplayOutput.java | 33 +-- .../outputs/display/NullDisplaySurface.java | 12 +- .../runtime/concurrent/ISimulationRunner.java | 69 ++++++ .../runtime/concurrent/SimulationRunner.java | 185 ++++++--------- .../concurrent/SimulationRunnerOld.java | 222 ++++++++++++++++++ .../extension/image/ImageDisplaySurface.java | 8 +- .../models/OBJ file drawing.gaml | 6 +- .../display/java2d/Java2DDisplaySurface.java | 31 ++- .../display/opengl/renderer/JOGLRenderer.java | 20 +- .../ui/display/opengl/scene/ModelScene.java | 2 +- .../opengl/view/SWTOpenGLDisplaySurface.java | 11 +- .../views/displays/LayeredDisplayView.java | 24 +- 19 files changed, 525 insertions(+), 258 deletions(-) create mode 100644 gama.core/src/gama/core/runtime/concurrent/ISimulationRunner.java create mode 100644 gama.core/src/gama/core/runtime/concurrent/SimulationRunnerOld.java diff --git a/gama.core/src/gama/core/common/interfaces/IDisplaySurface.java b/gama.core/src/gama/core/common/interfaces/IDisplaySurface.java index 436635ea08..c2d1c44d48 100644 --- a/gama.core/src/gama/core/common/interfaces/IDisplaySurface.java +++ b/gama.core/src/gama/core/common/interfaces/IDisplaySurface.java @@ -1,9 +1,9 @@ /******************************************************************************************************* * * IDisplaySurface.java, in gama.core, is part of the source code of the GAMA modeling and simulation platform - * . + * (v.2024-06). * - * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. * @@ -15,6 +15,7 @@ import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.util.Collection; +import java.util.concurrent.Semaphore; import org.locationtech.jts.geom.Envelope; @@ -23,8 +24,8 @@ import gama.core.metamodel.shape.GamaPoint; import gama.core.metamodel.shape.IShape; import gama.core.outputs.LayeredDisplayData; -import gama.core.outputs.LayeredDisplayOutput; import gama.core.outputs.LayeredDisplayData.DisplayDataListener; +import gama.core.outputs.LayeredDisplayOutput; import gama.core.outputs.layers.IEventLayerListener; import gama.core.runtime.IScope.IGraphicsScope; import gama.gaml.statements.draw.DrawingAttributes; @@ -108,9 +109,19 @@ public interface OpenGL extends IDisplaySurface { BufferedImage getImage(int width, int height); /** - * Asks the surface to update its display, optionnaly forcing it to do so (if it is paused, for instance) + * Asks the surface to update its display, optionnaly forcing it to do so (if it is paused, for instance). A + * synchronizer (possibly null) is passed, that needs to be released when the physical display is done **/ - void updateDisplay(boolean force); + void updateDisplay(boolean force, Semaphore synchronizer); + + /** + * Update display. + * + * @param force the force + */ + default void updateDisplay(final boolean force) { + updateDisplay(force, null); + } /** * Sets a concrete menu manager to be used for displaying menus on this surface @@ -325,16 +336,6 @@ default GamaPoint getModelCoordinatesFrom(final int xOnScreen, final int yOnScre */ int getFPS(); - /** - * @return true if the surface is considered as "realized" (i.e. displayed on the UI) - */ - // boolean isRealized(); - - /** - * @return true if the surface has been "rendered" (i.e. all the layers have been displayed) - */ - // boolean isRendered(); - /** * @return true if the surface has been 'disposed' already */ diff --git a/gama.core/src/gama/core/kernel/experiment/BatchAgent.java b/gama.core/src/gama/core/kernel/experiment/BatchAgent.java index eaa3a8d0e3..f12ca8edde 100644 --- a/gama.core/src/gama/core/kernel/experiment/BatchAgent.java +++ b/gama.core/src/gama/core/kernel/experiment/BatchAgent.java @@ -24,7 +24,6 @@ import gama.annotations.precompiler.GamlAnnotations.doc; import gama.annotations.precompiler.GamlAnnotations.experiment; import gama.core.common.interfaces.IKeyword; -import gama.core.common.interfaces.IScopedStepable; import gama.core.kernel.batch.exploration.AExplorationAlgorithm; import gama.core.kernel.batch.optimization.AOptimizationAlgorithm; import gama.core.kernel.experiment.IParameter.Batch; @@ -331,8 +330,7 @@ public IMap>> launchSimulationsWithSolut while (pop.hasScheduledSimulations() && !dead) { // We step all the simulations pop.step(getScope()); - for (final IScopedStepable st : new ArrayList<>(pop.getActiveStepables())) { - final SimulationAgent agent = (SimulationAgent) st; + for (final SimulationAgent agent : new ArrayList<>(pop.getRunningSimulations())) { ParametersSet ps = simToParameter.get(agent); currentSolution = new ParametersSet(ps); diff --git a/gama.core/src/gama/core/kernel/simulation/SimulationPopulation.java b/gama.core/src/gama/core/kernel/simulation/SimulationPopulation.java index a6562be264..f549667beb 100644 --- a/gama.core/src/gama/core/kernel/simulation/SimulationPopulation.java +++ b/gama.core/src/gama/core/kernel/simulation/SimulationPopulation.java @@ -1,9 +1,9 @@ /******************************************************************************************************* * * SimulationPopulation.java, in gama.core, is part of the source code of the GAMA modeling and simulation platform - * . + * (v.2024-06). * - * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. * @@ -17,7 +17,6 @@ import java.util.Set; import gama.core.common.interfaces.IKeyword; -import gama.core.common.interfaces.IScopedStepable; import gama.core.kernel.experiment.ExperimentAgent; import gama.core.kernel.experiment.ExperimentPlan; import gama.core.metamodel.agent.IAgent; @@ -28,8 +27,9 @@ import gama.core.runtime.GAMA; import gama.core.runtime.IScope; import gama.core.runtime.concurrent.GamaExecutorService; -import gama.core.runtime.concurrent.SimulationRunner; import gama.core.runtime.concurrent.GamaExecutorService.Caller; +import gama.core.runtime.concurrent.ISimulationRunner; +import gama.core.runtime.concurrent.SimulationRunner; import gama.core.runtime.exceptions.GamaRuntimeException; import gama.core.util.GamaListFactory; import gama.core.util.IList; @@ -48,7 +48,7 @@ public class SimulationPopulation extends GamaPopulation { private SimulationAgent currentSimulation; /** The runner. */ - private final SimulationRunner runner; + private final ISimulationRunner runner; /** * Instantiates a new simulation population. @@ -109,7 +109,8 @@ protected void fireAgentRemoved(final IScope scope, final IAgent old) { @Override public void initializeFor(final IScope scope) { - super.initializeFor(scope); + computeTopology(scope); + if (topology != null) { topology.initialize(scope, this); } this.currentAgentIndex = 0; } @@ -250,7 +251,7 @@ public void unscheduleSimulation(final SimulationAgent sim) { * * @return the number of active stepables */ - public Set getActiveStepables() { return runner.getStepable(); } + public Set getRunningSimulations() { return runner.getStepable(); } /** * Gets the number of active threads. diff --git a/gama.core/src/gama/core/outputs/AbstractOutput.java b/gama.core/src/gama/core/outputs/AbstractOutput.java index 062c6d7aae..433cd4c091 100644 --- a/gama.core/src/gama/core/outputs/AbstractOutput.java +++ b/gama.core/src/gama/core/outputs/AbstractOutput.java @@ -41,7 +41,7 @@ public abstract class AbstractOutput extends Symbol implements IOutput { private IScope outputScope; /** The permanent. */ - boolean paused, open, permanent = false; + volatile boolean paused, open, permanent, disposed = false; /** The is user created. */ private boolean isUserCreated = true; @@ -58,12 +58,6 @@ public abstract class AbstractOutput extends Symbol implements IOutput { /** The virtual. */ final boolean virtual; - /** The rendered. */ - volatile boolean rendered; - - /** The disposed. */ - protected boolean disposed = false; - /** The view. */ protected IGamaView view; @@ -247,9 +241,6 @@ void setPermanent() { permanent = true; } - @Override - public void setRendered(final boolean b) { rendered = b; } - /** * Checks if is permanent. * @@ -275,13 +266,6 @@ protected boolean shouldOpenView() { @Override public IGamaView getView() { return view; } - @Override - public boolean isRendered() { - if (view != null && !view.isVisible()) return true; - if (!this.isRefreshable() || !this.isOpen() || this.isPaused()) return true; - return rendered; - } - @Override public void dispose() { if (disposed) return; diff --git a/gama.core/src/gama/core/outputs/AbstractOutputManager.java b/gama.core/src/gama/core/outputs/AbstractOutputManager.java index 579f40a91c..f050d6ce5d 100644 --- a/gama.core/src/gama/core/outputs/AbstractOutputManager.java +++ b/gama.core/src/gama/core/outputs/AbstractOutputManager.java @@ -28,7 +28,6 @@ import gama.core.util.GamaMapFactory; import gama.core.util.IMap; import gama.dev.DEBUG; -import gama.dev.THREADS; import gama.gaml.compilation.ISymbol; import gama.gaml.compilation.Symbol; import gama.gaml.descriptions.IDescription; @@ -63,7 +62,8 @@ public abstract class AbstractOutputManager extends Symbol implements IOutputMan LayoutStatement layout; /** The outputs. */ - protected final Map outputs = GamaMapFactory.synchronizedOrderedMap(); + protected final Map outputs = GamaMapFactory.create(); + // GamaMapFactory.synchronizedOrderedMap(); // protected final IList monitors = GamaListFactory.create(); @@ -118,12 +118,7 @@ public void add(final IOutput output) { hasMonitors |= output instanceof MonitorOutput; if (output.isVirtual()) { virtualOutputs.put(output.getId(), output); - } - // else if (output instanceof MonitorOutput monitor - // && GamaPreferences.Interface.CORE_MONITOR_PARAMETERS.getValue()) { - // monitors.add(monitor); - // } - else { + } else { synchronized (outputs) { outputs.put(output.getId(), output); } @@ -137,10 +132,9 @@ public synchronized void dispose() { // AD: explicit addition of an ArrayList to prevent dispose errors // (when outputs remove themselves from the list) GAMA.desynchronizeFrontmostExperiment(); - synchronized (outputs) { - for (final IOutput output : new ArrayList<>(outputs.values())) { output.dispose(); } - } - // for (final IOutput output : new ArrayList<>(monitors)) { output.dispose(); } + // synchronized (outputs) { + for (final IOutput output : new ArrayList<>(outputs.values())) { output.dispose(); } + // } clear(); } catch (final Exception e) { e.printStackTrace(); @@ -303,27 +297,12 @@ protected boolean initialStep(final IScope scope, final IOutput output) { @Override public boolean step(final IScope scope) { - getOutputs().forEach((n, each) -> { each.setRendered(false); }); outputs.forEach((name, each) -> { if (each instanceof LayeredDisplayOutput ldo) { ldo.linkScopeWithGraphics(); } if (each.isRefreshable() && each.getScope().step(each).passed()) { each.update(); } }); - if (GAMA.isSynchronized() && !inInitPhase) { - while (!allOutputsRendered()) { - THREADS.WAIT(20, "The outputs are not rendered yet", "AbstractOutputManager.step() interrupted"); - } - } - evaluateAutoSave(scope); - return true; - } - /** - * All outputs rendered. - * - * @return true, if successful - */ - protected boolean allOutputsRendered() { - for (IOutput each : outputs.values()) { if (!each.isRendered()) return false; } + evaluateAutoSave(scope); return true; } diff --git a/gama.core/src/gama/core/outputs/AbstractValuedDisplayOutput.java b/gama.core/src/gama/core/outputs/AbstractValuedDisplayOutput.java index 7b168e75a9..da252588d4 100644 --- a/gama.core/src/gama/core/outputs/AbstractValuedDisplayOutput.java +++ b/gama.core/src/gama/core/outputs/AbstractValuedDisplayOutput.java @@ -42,9 +42,6 @@ public AbstractValuedDisplayOutput(final IDescription desc) { expressionText = getValue() == null ? "" : getValue().serializeToGaml(false); } - @Override - public boolean isRendered() { return true; } - /** * Gets the last value. * diff --git a/gama.core/src/gama/core/outputs/IOutput.java b/gama.core/src/gama/core/outputs/IOutput.java index 6c04b91677..69a1a974c6 100644 --- a/gama.core/src/gama/core/outputs/IOutput.java +++ b/gama.core/src/gama/core/outputs/IOutput.java @@ -18,7 +18,7 @@ /** * This interface represents the objects, declared in a model, which perform various types of computations and return - * information supposed to be displayed or saved during simulations. Outputs are not in charge of displaying/outputting + * information supposed to be displayed during simulations. Outputs are not in charge of displaying/outputting * information on a concrete support, only computing it. They however control whatever concrete support they represent * (opening, closing, pausing, updating and refreshing it). * @@ -48,8 +48,8 @@ public interface IOutput extends ISymbol, IStepable, IScoped { boolean isPaused(); /** - * In response to this message, the output is supposed to open its concrete support, whether it is a view or a file. - * Sending open() to an already opened output should not have any effect. + * In response to this message, the output is supposed to open its concrete support. Sending open() to an already + * opened output should not have any effect. */ void open(); @@ -61,8 +61,8 @@ public interface IOutput extends ISymbol, IStepable, IScoped { boolean isOpen(); /** - * In response to this message, the output is supposed to close its concrete support, whether it is a view or a - * file. A closed output cannot resume its operations unless 'open()' is called again. + * In response to this message, the output is supposed to close its concrete support. A closed output cannot resume + * its operations unless 'open()' is called again. */ void close(); @@ -100,7 +100,7 @@ public interface IOutput extends ISymbol, IStepable, IScoped { /** * Returns the original name of the output (as it has been declared by the modeler). This name can be changed later - * to accomoadate different display configuration in the UI + * to accomodate different display configuration in the UI * * @return the string representing the original (unaltered) name of the output as defined by the modeler */ @@ -109,7 +109,7 @@ public interface IOutput extends ISymbol, IStepable, IScoped { /** * Returns the identifier (should be unique) of this output * - * @return a string representing the unique identifier of this output (especially important for UI outputs) + * @return a string representing the unique identifier of this output */ String getId(); @@ -131,8 +131,8 @@ public interface IOutput extends ISymbol, IStepable, IScoped { void setUserCreated(boolean b); /** - * If only one output of this kind is allowed in the UI (i.e. there can only be one instance of the corresponding - * view), the output should return true + * If only one output of this kind is allowed (i.e. there can only be one instance of the corresponding concrete + * support), the output should return true * * @return true if only one view for this kind of output is possible, false otherwise */ @@ -156,31 +156,33 @@ public interface IOutput extends ISymbol, IStepable, IScoped { boolean isVirtual(); /** - * Checks if is auto save. + * Checks if is auto save. This default method always returns false. * * @return true, if is auto save */ default boolean isAutoSave() { return false; } /** - * Returns the GamaView associated with this output - */ - - IGamaView getView(); - - /** - * Checks if is rendered. + * Returns the GamaView associated with this output, if any * - * @return true, if is rendered + * @return an instance of IGamaView or null if no view is associated to this output */ - boolean isRendered(); - /** - * Sets the rendered. - * - * @param b - * the new rendered - */ - void setRendered(boolean b); + IGamaView getView(); + // + // /** + // * Checks if this output has been rendered. This default method always returns true. + // * + // * @return true, if is rendered + // */ + // default boolean isRendered() { return true; } + // + // /** + // * Sent by the view or any other representation of this output, to say whether it has been rendered already or not + // * + // * @param b + // * the new rendered + // */ + // default void setRendered(final boolean b) {} } diff --git a/gama.core/src/gama/core/outputs/LayeredDisplayOutput.java b/gama.core/src/gama/core/outputs/LayeredDisplayOutput.java index 86da2f309d..f11eef22e8 100644 --- a/gama.core/src/gama/core/outputs/LayeredDisplayOutput.java +++ b/gama.core/src/gama/core/outputs/LayeredDisplayOutput.java @@ -624,7 +624,7 @@ public void update() throws GamaRuntimeException { super.update(); // See #3696 - if (!surface.shouldWaitToBecomeRendered()) { setRendered(true); } + // if (!surface.shouldWaitToBecomeRendered()) { setRendered(true); } } @Override @@ -752,7 +752,7 @@ public void setPaused(final boolean paused) { super.setPaused(paused); if (surface == null) return; if (getData().is3D()) { ((IDisplaySurface.OpenGL) surface).setPaused(paused); } - if (wasPaused && !paused) { surface.updateDisplay(false); } + if (wasPaused && !paused) { surface.updateDisplay(false, null); } } /** @@ -764,25 +764,6 @@ public LayeredDisplayData getData() { return data; // .get(); } - // Keeping in sync the two implementations of synchronized, so that OpenGL - // objects can have an easy access to the value (and modify it). Also allows - // modelers to declare this property directly in the model. - - // @Override - // public void setSynchronized(final boolean sync) { - // // getData().setSynchronized(sync); - // super.setSynchronized(sync); - // - // } - - /** - * Checks if is synchronized. - * - * @return true, if is synchronized - */ - // @Override - // public boolean isSynchronized() { return super.isSynchronized() && getData().isSynchronized(); } - /** * Gets the index. * @@ -853,4 +834,14 @@ public void linkScopeWithGraphics() { if (scope == null || surface == null) return; scope.setGraphics(surface.getIGraphics()); } + + // @Override + // public void setRendered(final boolean b) { rendered = b; } + // + // @Override + // public boolean isRendered() { + // if (view != null && !view.isVisible()) return true; + // if (!this.isRefreshable() || !this.isOpen() || this.isPaused()) return true; + // return rendered; + // } } diff --git a/gama.core/src/gama/core/outputs/display/NullDisplaySurface.java b/gama.core/src/gama/core/outputs/display/NullDisplaySurface.java index e9b00e3ed5..1989deb832 100644 --- a/gama.core/src/gama/core/outputs/display/NullDisplaySurface.java +++ b/gama.core/src/gama/core/outputs/display/NullDisplaySurface.java @@ -1,9 +1,9 @@ /******************************************************************************************************* * * NullDisplaySurface.java, in gama.core, is part of the source code of the GAMA modeling and simulation platform - * . + * (v.2024-06). * - * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. * @@ -14,6 +14,7 @@ import java.awt.image.BufferedImage; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.Semaphore; import org.locationtech.jts.geom.Envelope; @@ -24,8 +25,8 @@ import gama.core.metamodel.agent.IAgent; import gama.core.metamodel.shape.IShape; import gama.core.outputs.LayeredDisplayData; -import gama.core.outputs.LayeredDisplayOutput; import gama.core.outputs.LayeredDisplayData.Changes; +import gama.core.outputs.LayeredDisplayOutput; import gama.core.outputs.layers.IEventLayerListener; import gama.core.runtime.IScope.IGraphicsScope; import gama.core.util.IList; @@ -66,7 +67,7 @@ public void dispose() {} * @see gama.core.common.interfaces.IDisplaySurface#updateDisplay() */ @Override - public void updateDisplay(final boolean force) {} + public void updateDisplay(final boolean force, final Semaphore synchronizer) {} /** * Method zoomIn() @@ -135,7 +136,8 @@ public void focusOn(final IShape geometry) {} /** * Method initialize() * - * @see gama.core.common.interfaces.IDisplaySurface#initialize(double, double, gama.core.outputs.LayeredDisplayOutput) + * @see gama.core.common.interfaces.IDisplaySurface#initialize(double, double, + * gama.core.outputs.LayeredDisplayOutput) */ @Override public void outputReloaded() {} diff --git a/gama.core/src/gama/core/runtime/concurrent/ISimulationRunner.java b/gama.core/src/gama/core/runtime/concurrent/ISimulationRunner.java new file mode 100644 index 0000000000..7062b90656 --- /dev/null +++ b/gama.core/src/gama/core/runtime/concurrent/ISimulationRunner.java @@ -0,0 +1,69 @@ +/******************************************************************************************************* + * + * ISimulationRunner.java, in gama.core, is part of the source code of the GAMA modeling and simulation platform + * (v.2024-06). + * + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) + * + * Visit https://github.com/gama-platform/gama for license information and contacts. + * + ********************************************************************************************************/ +package gama.core.runtime.concurrent; + +import java.util.Set; + +import gama.core.kernel.simulation.SimulationAgent; + +/** + * The Interface ISimulationRunner. + */ +public interface ISimulationRunner { + + /** + * Removes the. + * + * @param agent + * the agent + */ + void remove(SimulationAgent agent); + + /** + * Adds the. + * + * @param agent + * the agent + */ + void add(SimulationAgent agent); + + /** + * Step. + */ + void step(); + + /** + * Dispose. + */ + void dispose(); + + /** + * Gets the active stepables. + * + * @return the active stepables + */ + Set getStepable(); + + /** + * Gets the active threads. + * + * @return the active threads + */ + int getActiveThreads(); + + /** + * Checks for simulations. + * + * @return true, if successful + */ + boolean hasSimulations(); + +} \ No newline at end of file diff --git a/gama.core/src/gama/core/runtime/concurrent/SimulationRunner.java b/gama.core/src/gama/core/runtime/concurrent/SimulationRunner.java index 4537f264ce..9abc5966fe 100644 --- a/gama.core/src/gama/core/runtime/concurrent/SimulationRunner.java +++ b/gama.core/src/gama/core/runtime/concurrent/SimulationRunner.java @@ -1,9 +1,9 @@ /******************************************************************************************************* * * SimulationRunner.java, in gama.core, is part of the source code of the GAMA modeling and simulation platform - * . + * (v.2024-06). * - * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. * @@ -11,43 +11,44 @@ package gama.core.runtime.concurrent; import static gama.core.runtime.concurrent.GamaExecutorService.EXCEPTION_HANDLER; -import static gama.core.runtime.concurrent.GamaExecutorService.THREADS_NUMBER; import static gama.core.runtime.concurrent.GamaExecutorService.getParallelism; -import static java.util.concurrent.Executors.newSingleThreadExecutor; -import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; - -import gama.core.common.interfaces.IScopedStepable; +import java.util.concurrent.Semaphore; + import gama.core.kernel.experiment.IExperimentPlan; +import gama.core.kernel.simulation.SimulationAgent; import gama.core.kernel.simulation.SimulationPopulation; import gama.core.runtime.concurrent.GamaExecutorService.Caller; +import gama.dev.DEBUG; /** * The Class SimulationRunner. */ -public class SimulationRunner { +public class SimulationRunner implements ISimulationRunner { - /** The executor. */ - public volatile ExecutorService executor; + static { + DEBUG.OFF(); + } /** The runnables. */ - final Map> runnables; + final Map runnables; + + /** The simulationsSemaphore. */ + final Object lock = new Object(); + /** The simulationsSemaphore. */ + final Semaphore simulationsSemaphore = new Semaphore(0); + + /** The experiment semaphore. */ + final Semaphore experimentSemaphore = new Semaphore(0); /** The concurrency. */ final int concurrency; - /** The active threads. */ - volatile int activeThreads; + /** The shutdown. */ + volatile boolean shutdown = false; /** * Of. @@ -64,17 +65,6 @@ public static SimulationRunner of(final SimulationPopulation pop) { } else { concurrency = getParallelism(pop.getHost().getScope(), plan.getConcurrency(), Caller.SIMULATION); } - return withConcurrency(concurrency); - } - - /** - * With concurrency. - * - * @param concurrency - * the concurrency - * @return the simulation runner - */ - public static SimulationRunner withConcurrency(final int concurrency) { return new SimulationRunner(concurrency < 0 ? 1 : concurrency); } @@ -95,7 +85,8 @@ private SimulationRunner(final int concurrency) { * @param agent * the agent */ - public void remove(final IScopedStepable agent) { + @Override + public void remove(final SimulationAgent agent) { runnables.remove(agent); } @@ -105,63 +96,68 @@ public void remove(final IScopedStepable agent) { * @param agent * the agent */ - public void add(final IScopedStepable agent) { - add(agent, () -> { - activeThreads = computeNumberOfThreads(); - return agent.step(); - }); - } + @Override + public void add(final SimulationAgent agent) { + DEBUG.OUT("Adding " + agent); + Thread t = new Thread("Thread of " + agent.getName()) { + @Override + public void run() { + DEBUG.OUT("Launching " + agent + " shutdown = " + shutdown); + while (!shutdown) { + + // synchronized (lock) { + try { + DEBUG.OUT("Waiting for " + agent); + // lock.wait(); + simulationsSemaphore.acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // } + try { + agent.step(); + experimentSemaphore.release(); + } catch (Throwable tg) { + EXCEPTION_HANDLER.uncaughtException(Thread.currentThread(), tg); + } + } + } + }; + t.start(); + runnables.put(agent, t); - /** - * Adds the. - * - * @param agent - * the agent - * @param callable - * the callable - */ - public void add(final IScopedStepable agent, final Callable callable) { - runnables.put(agent, callable); } /** * Step. */ + @Override public void step() { + // synchronized (lock) { + DEBUG.OUT("Releasing to all simulations"); + int nb = getActiveThreads(); + simulationsSemaphore.release(nb); try { - getExecutor().invokeAll(runnables.values()); - } catch (final InterruptedException e) { - + experimentSemaphore.acquire(nb); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); } - - } - - /** - * Compute number of threads. - * - * @return the int - */ - private int computeNumberOfThreads() { - return getExecutor() instanceof ThreadPoolExecutor tpe ? Math.min(concurrency, tpe.getActiveCount()) : 1; - } - - /** - * Gets the executor. - * - * @return the executor - */ - protected ExecutorService getExecutor() { - return executor == null - ? executor = concurrency == 0 ? newSingleThreadExecutor() : new Executor(THREADS_NUMBER.getValue()) - : executor; + // lock.notifyAll(); + // } } /** * Dispose. */ + @Override public void dispose() { + shutdown = true; + int nb = runnables.size(); runnables.clear(); - if (executor != null) { executor.shutdownNow(); } + DEBUG.OUT("Disposing simulation runner and releasing " + nb + " threads"); + experimentSemaphore.release(nb); + simulationsSemaphore.release(nb); } /** @@ -169,58 +165,25 @@ public void dispose() { * * @return the active stepables */ - public Set getStepable() { return runnables.keySet(); } + @Override + public Set getStepable() { return runnables.keySet(); } /** * Gets the active threads. * * @return the active threads */ - public int getActiveThreads() { return activeThreads; } + @Override + public int getActiveThreads() { return runnables.size(); } /** * Checks for simulations. * * @return true, if successful */ + @Override public boolean hasSimulations() { return runnables.size() > 0; } - /** - * The Class Executor. - */ - static class Executor extends ThreadPoolExecutor { - - /** - * Instantiates a new executor. - * - * @param nb - * the nb - */ - Executor(final int nb) { - super(nb, nb, 0L, MILLISECONDS, new LinkedBlockingQueue<>()); - } - - @Override - protected void afterExecute(final Runnable r, final Throwable exception) { - Throwable t = exception; - super.afterExecute(r, t); - if (t == null && r instanceof Future) { - try { - final Future future = (Future) r; - if (future.isDone()) { future.get(); } - } catch (final CancellationException ce) { - t = ce; - } catch (final ExecutionException ee) { - t = ee.getCause(); - } catch (final InterruptedException ie) { - Thread.currentThread().interrupt(); // ignore/reset - } - } - if (t != null) { EXCEPTION_HANDLER.uncaughtException(Thread.currentThread(), t); } - } - - } - } diff --git a/gama.core/src/gama/core/runtime/concurrent/SimulationRunnerOld.java b/gama.core/src/gama/core/runtime/concurrent/SimulationRunnerOld.java new file mode 100644 index 0000000000..844fd6feda --- /dev/null +++ b/gama.core/src/gama/core/runtime/concurrent/SimulationRunnerOld.java @@ -0,0 +1,222 @@ +/******************************************************************************************************* + * + * SimulationRunnerOld.java, in gama.core, is part of the source code of the GAMA modeling and simulation platform + * (v.2024-06). + * + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) + * + * Visit https://github.com/gama-platform/gama for license information and contacts. + * + ********************************************************************************************************/ +package gama.core.runtime.concurrent; + +import static gama.core.runtime.concurrent.GamaExecutorService.EXCEPTION_HANDLER; +import static gama.core.runtime.concurrent.GamaExecutorService.THREADS_NUMBER; +import static gama.core.runtime.concurrent.GamaExecutorService.getParallelism; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; + +import gama.core.kernel.experiment.IExperimentPlan; +import gama.core.kernel.simulation.SimulationAgent; +import gama.core.kernel.simulation.SimulationPopulation; +import gama.core.runtime.concurrent.GamaExecutorService.Caller; + +/** + * The Class SimulationRunner. + */ +public class SimulationRunnerOld implements ISimulationRunner { + + /** The executor. */ + public volatile ExecutorService executor; + + /** The runnables. */ + final Map> runnables; + + /** The concurrency. */ + final int concurrency; + + /** The active threads. */ + volatile int activeThreads; + + /** + * Of. + * + * @param pop + * the pop + * @return the simulation runner + */ + public static ISimulationRunner of(final SimulationPopulation pop) { + int concurrency = 0; + final IExperimentPlan plan = pop.getHost().getSpecies(); + if (plan.isHeadless() && !plan.isBatch()) { + concurrency = 1; + } else { + concurrency = getParallelism(pop.getHost().getScope(), plan.getConcurrency(), Caller.SIMULATION); + } + return new SimulationRunnerOld(concurrency < 0 ? 1 : concurrency); + } + + /** + * Instantiates a new simulation runner. + * + * @param concurrency + * the concurrency + */ + private SimulationRunnerOld(final int concurrency) { + this.concurrency = concurrency; + runnables = new LinkedHashMap<>(); + } + + /** + * Removes the. + * + * @param agent + * the agent + */ + @Override + public void remove(final SimulationAgent agent) { + runnables.remove(agent); + } + + /** + * Adds the. + * + * @param agent + * the agent + */ + @Override + public void add(final SimulationAgent agent) { + add(agent, () -> { + activeThreads = computeNumberOfThreads(); + return agent.step(); + }); + } + + /** + * Adds the. + * + * @param agent + * the agent + * @param callable + * the callable + */ + private void add(final SimulationAgent agent, final Callable callable) { + runnables.put(agent, callable); + } + + /** + * Step. + */ + @Override + public void step() { + try { + getExecutor().invokeAll(runnables.values()); + } catch (final InterruptedException e) { + + } + + } + + /** + * Compute number of threads. + * + * @return the int + */ + private int computeNumberOfThreads() { + return getExecutor() instanceof ThreadPoolExecutor tpe ? Math.min(concurrency, tpe.getActiveCount()) : 1; + } + + /** + * Gets the executor. + * + * @return the executor + */ + protected ExecutorService getExecutor() { + return executor == null + ? executor = concurrency == 0 ? newSingleThreadExecutor() : new Executor(THREADS_NUMBER.getValue()) + : executor; + } + + /** + * Dispose. + */ + @Override + public void dispose() { + runnables.clear(); + if (executor != null) { executor.shutdownNow(); } + } + + /** + * Gets the active stepables. + * + * @return the active stepables + */ + @Override + public Set getStepable() { return runnables.keySet(); } + + /** + * Gets the active threads. + * + * @return the active threads + */ + @Override + public int getActiveThreads() { return activeThreads; } + + /** + * Checks for simulations. + * + * @return true, if successful + */ + @Override + public boolean hasSimulations() { + return runnables.size() > 0; + } + + /** + * The Class Executor. + */ + static class Executor extends ThreadPoolExecutor { + + /** + * Instantiates a new executor. + * + * @param nb + * the nb + */ + Executor(final int nb) { + super(nb, nb, 0L, MILLISECONDS, new LinkedBlockingQueue<>()); + } + + @Override + protected void afterExecute(final Runnable r, final Throwable exception) { + Throwable t = exception; + super.afterExecute(r, t); + if (t == null && r instanceof Future) { + try { + final Future future = (Future) r; + if (future.isDone()) { future.get(); } + } catch (final CancellationException ce) { + t = ce; + } catch (final ExecutionException ee) { + t = ee.getCause(); + } catch (final InterruptedException ie) { + Thread.currentThread().interrupt(); // ignore/reset + } + } + if (t != null) { EXCEPTION_HANDLER.uncaughtException(Thread.currentThread(), t); } + } + + } + +} diff --git a/gama.extension.image/src/gama/extension/image/ImageDisplaySurface.java b/gama.extension.image/src/gama/extension/image/ImageDisplaySurface.java index 6658fbcdc4..6fc2db3f08 100644 --- a/gama.extension.image/src/gama/extension/image/ImageDisplaySurface.java +++ b/gama.extension.image/src/gama/extension/image/ImageDisplaySurface.java @@ -1,9 +1,9 @@ /******************************************************************************************************* * * ImageDisplaySurface.java, in gama.extension.image, is part of the source code of the GAMA modeling and simulation - * platform . + * platform (v.2024-06). * - * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. * @@ -16,6 +16,7 @@ import java.awt.Rectangle; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.Semaphore; import org.locationtech.jts.geom.Envelope; @@ -140,8 +141,9 @@ public boolean resizeImage(final int newWidth, final int newHeight, final boolea } @Override - public void updateDisplay(final boolean force) { + public void updateDisplay(final boolean force, final Semaphore synchronizer) { drawAllDisplays(); + if (synchronizer != null) synchronizer.release(); } /** diff --git a/gama.library/models/Data/Data Importation/models/OBJ file drawing.gaml b/gama.library/models/Data/Data Importation/models/OBJ file drawing.gaml index f7ff592d67..9d7b6b0e60 100644 --- a/gama.library/models/Data/Data Importation/models/OBJ file drawing.gaml +++ b/gama.library/models/Data/Data Importation/models/OBJ file drawing.gaml @@ -20,8 +20,8 @@ species object skills: [moving]{ rgb color <- rgb(rnd(255),rnd(255),rnd(255)); int size <- rnd(10) + 1; int rot <- 1000 + rnd(1000); - reflex m when: every(100#cycles) { - do wander amplitude: 30.0 speed: 0.001; + reflex m { + do wander amplitude: 30.0 speed: 1; } aspect obj { draw obj_file("../includes/teapot.obj") color: color size: size rotate: cycle/rot::{0,1,0} ; @@ -29,7 +29,7 @@ species object skills: [moving]{ } experiment Display type: gui { - output { + output synchronized: true{ display ComplexObject type: 3d background:#orange{ species object aspect:obj; } diff --git a/gama.ui.display.java2d/src/gama/ui/display/java2d/Java2DDisplaySurface.java b/gama.ui.display.java2d/src/gama/ui/display/java2d/Java2DDisplaySurface.java index 5a05ad60d9..1903552736 100644 --- a/gama.ui.display.java2d/src/gama/ui/display/java2d/Java2DDisplaySurface.java +++ b/gama.ui.display.java2d/src/gama/ui/display/java2d/Java2DDisplaySurface.java @@ -1,9 +1,9 @@ /******************************************************************************************************* * * Java2DDisplaySurface.java, in gama.ui.display.java2d, is part of the source code of the GAMA modeling and simulation - * platform . + * platform (v.2024-06). * - * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU) + * (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) * * Visit https://github.com/gama-platform/gama for license information and contacts. * @@ -47,15 +47,15 @@ import gama.core.metamodel.shape.GamaPoint; import gama.core.metamodel.shape.IShape; import gama.core.outputs.LayeredDisplayData; -import gama.core.outputs.LayeredDisplayOutput; import gama.core.outputs.LayeredDisplayData.Changes; +import gama.core.outputs.LayeredDisplayOutput; import gama.core.outputs.display.AWTDisplayGraphics; import gama.core.outputs.display.LayerManager; import gama.core.outputs.layers.IEventLayerListener; import gama.core.outputs.layers.OverlayLayer; import gama.core.runtime.GAMA; -import gama.core.runtime.PlatformHelper; import gama.core.runtime.IScope.IGraphicsScope; +import gama.core.runtime.PlatformHelper; import gama.dev.DEBUG; import gama.dev.THREADS; import gama.extension.image.GamaImage; @@ -375,10 +375,27 @@ void setOrigin(final int x, final int y) { } @Override - public void updateDisplay(final boolean force) { + public void updateDisplay(final boolean force, final Semaphore synchronizer) { if (disposed) return; rendered = false; - EventQueue.invokeLater(this::repaint); + Runnable toRun = () -> { + repaint(); + if (synchronizer != null) { synchronizer.release(); } + }; + if (GAMA.isSynchronized()) { + + if (EventQueue.isDispatchThread()) { + toRun.run(); + } else { + try { + EventQueue.invokeAndWait(toRun); + } catch (InvocationTargetException | InterruptedException e) { + e.printStackTrace(); + } + } + } else { + EventQueue.invokeLater(toRun); + } } @Override @@ -510,7 +527,7 @@ public void paintComponent(final Graphics g) { g2d.dispose(); frames++; rendered = true; - getOutput().setRendered(true); + // getOutput().setRendered(true); } /** diff --git a/gama.ui.display.opengl/src/gama/ui/display/opengl/renderer/JOGLRenderer.java b/gama.ui.display.opengl/src/gama/ui/display/opengl/renderer/JOGLRenderer.java index 06745b46db..021954f41e 100644 --- a/gama.ui.display.opengl/src/gama/ui/display/opengl/renderer/JOGLRenderer.java +++ b/gama.ui.display.opengl/src/gama/ui/display/opengl/renderer/JOGLRenderer.java @@ -14,6 +14,7 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.List; +import java.util.concurrent.Semaphore; import org.locationtech.jts.geom.Geometry; @@ -208,6 +209,7 @@ public void display(final GLAutoDrawable drawable) { cameraHelper.update(); lightHelper.draw(); sceneHelper.draw(); + if (synchronizer != null) { synchronizer.release(); } } // // if (!visible) { @@ -227,6 +229,9 @@ public void display(final GLAutoDrawable drawable) { /** The first. */ boolean first = true; + /** The synchronizer. */ + private Semaphore synchronizer; + @Override public void reshape(final GLAutoDrawable drawable, final int arg1, final int arg2, final int w, final int h) { int width = DPIHelper.autoScaleDown(getCanvas().getMonitor(), w), @@ -240,7 +245,7 @@ public void reshape(final GLAutoDrawable drawable, final int arg1, final int arg keystoneHelper.reshape(width, height); openGL.reshape(gl, width, height); // sceneHelper.reshape(width, height); - surface.updateDisplay(true); + surface.updateDisplay(true, synchronizer); getCanvas().updateVisibleStatus(getCanvas().isVisible()); } @@ -253,6 +258,7 @@ public void dispose(final GLAutoDrawable drawable) { cameraHelper.dispose(); drawable.removeGLEventListener(this); disposed = true; + // surface.getOutput().setRendered(true); } /** @@ -538,4 +544,16 @@ public boolean hasDrawnOnce() { return !first; } + /** + * Sets the synchronizer. + * + * @param synchronizer + * the new synchronizer + */ + public void setSynchronizer(final Semaphore synchronizer) { + if (synchronizer == null) return; + this.synchronizer = synchronizer; + + } + } diff --git a/gama.ui.display.opengl/src/gama/ui/display/opengl/scene/ModelScene.java b/gama.ui.display.opengl/src/gama/ui/display/opengl/scene/ModelScene.java index 11ace8fdb6..ae66d698a9 100644 --- a/gama.ui.display.opengl/src/gama/ui/display/opengl/scene/ModelScene.java +++ b/gama.ui.display.opengl/src/gama/ui/display/opengl/scene/ModelScene.java @@ -147,7 +147,7 @@ public void draw(final OpenGL gl) { gl.setZIncrement(0); rendered = true; - renderer.getSurface().getOutput().setRendered(true); + // renderer.getSurface().getOutput().setRendered(true); gl.pop(GLMatrixFunc.GL_MODELVIEW); } diff --git a/gama.ui.display.opengl/src/gama/ui/display/opengl/view/SWTOpenGLDisplaySurface.java b/gama.ui.display.opengl/src/gama/ui/display/opengl/view/SWTOpenGLDisplaySurface.java index 6b1aee1c72..6af862a592 100644 --- a/gama.ui.display.opengl/src/gama/ui/display/opengl/view/SWTOpenGLDisplaySurface.java +++ b/gama.ui.display.opengl/src/gama/ui/display/opengl/view/SWTOpenGLDisplaySurface.java @@ -27,6 +27,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.Semaphore; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MenuEvent; @@ -68,7 +69,6 @@ import gama.extension.image.GamaImage; import gama.extension.image.ImageHelper; import gama.gaml.statements.draw.DrawingAttributes; -import gama.ui.display.opengl.renderer.IOpenGLRenderer; import gama.ui.display.opengl.renderer.JOGLRenderer; import gama.ui.experiment.menus.AgentsMenu; import gama.ui.experiment.views.displays.DisplaySurfaceMenu; @@ -98,7 +98,7 @@ public class SWTOpenGLDisplaySurface implements IDisplaySurface.OpenGL { GLAnimatorControl animator; /** The renderer. */ - IOpenGLRenderer renderer; + JOGLRenderer renderer; /** The zoom fit. */ protected boolean zoomFit = true; @@ -160,7 +160,7 @@ public SWTOpenGLDisplaySurface(final Object... objects) { * * @return the i open GL renderer */ - protected IOpenGLRenderer createRenderer() { + protected JOGLRenderer createRenderer() { return new JOGLRenderer(this); } @@ -266,10 +266,11 @@ public int getElem(final int bank, final int i) { * @see gama.core.common.interfaces.IDisplaySurface#updateDisplay(boolean) */ @Override - public void updateDisplay(final boolean force) { + public void updateDisplay(final boolean force, final Semaphore synchronizer) { if (alreadyUpdating) return; try { alreadyUpdating = true; + renderer.setSynchronizer(synchronizer); layerManager.drawLayersOn(renderer); } finally { alreadyUpdating = false; @@ -685,7 +686,7 @@ public void dispose() { this.renderer = null; GAMA.releaseScope(getScope()); setDisplayScope(null); - if (getOutput() != null) { getOutput().setRendered(true); } + // if (getOutput() != null) { getOutput().setRendered(true); } } @Override diff --git a/gama.ui.experiment/src/gama/ui/experiment/views/displays/LayeredDisplayView.java b/gama.ui.experiment/src/gama/ui/experiment/views/displays/LayeredDisplayView.java index aca4612898..2b3b699b09 100644 --- a/gama.ui.experiment/src/gama/ui/experiment/views/displays/LayeredDisplayView.java +++ b/gama.ui.experiment/src/gama/ui/experiment/views/displays/LayeredDisplayView.java @@ -11,6 +11,7 @@ package gama.ui.experiment.views.displays; import java.awt.Color; +import java.util.concurrent.Semaphore; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -85,6 +86,12 @@ public abstract class LayeredDisplayView extends GamaViewPart /** The central panel. */ protected CentralPanel centralPanel; + /** + * The display semaphore. Acquired when the view is updating, supposed to be released when the actual surface has + * been rendered + */ + protected Semaphore displaySemaphore = new Semaphore(1); + @Override public void setIndex(final int index) { realIndex = index; } @@ -344,7 +351,7 @@ protected IStatus run(final IProgressMonitor monitor) { final IDisplaySurface surface = getDisplaySurface(); if (surface != null && !disposed && !surface.isDisposed()) { try { - surface.updateDisplay(false); + surface.updateDisplay(false, displaySemaphore); } catch (Exception e) { DEBUG.OUT("Error when updating " + getTitle() + ": " + e.getMessage()); } @@ -362,7 +369,18 @@ protected IStatus run(final IProgressMonitor monitor) { */ @Override public void update(final IOutput output) { - super.update(output); + final Job job = getUpdateJob(); + job.schedule(); + if (GAMA.isSynchronized() && !WorkbenchHelper.isDisplayThread()) { + try { + // Shoyld We put a time out in case ? + // displaySemaphore.tryAcquire(5, TimeUnit.SECONDS); + displaySemaphore.acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + updateSnapshot(); } @@ -425,6 +443,7 @@ public void showOverlay(final boolean show) { @Override public void takeSnapshot(final GamaPoint customDimensions) { GAMA.getSnapshotMaker().takeAndSaveSnapshot(getDisplaySurface(), customDimensions); + } /** @@ -450,6 +469,7 @@ public void close(final IScope scope) { @Override public void dispose() { + displaySemaphore.release(); WorkbenchHelper.run(() -> { if (getSite() != null) { getSite().getShell().removeControlListener(shellListener); } super.dispose();