]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ProfileObserver.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.scenegraph.profile / src / org / simantics / scenegraph / profile / common / ProfileObserver.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.scenegraph.profile.common;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.concurrent.TimeUnit;
20
21 import org.simantics.Simantics;
22 import org.simantics.db.AsyncRequestProcessor;
23 import org.simantics.db.Resource;
24 import org.simantics.db.Session;
25 import org.simantics.db.common.session.SessionEventListenerAdapter;
26 import org.simantics.db.procedure.Procedure;
27 import org.simantics.db.service.QueryControl;
28 import org.simantics.db.service.SessionEventSupport;
29 import org.simantics.scenegraph.INode;
30 import org.simantics.scenegraph.g2d.G2DSceneGraph;
31 import org.simantics.scenegraph.profile.EvaluationContext;
32 import org.simantics.scenegraph.profile.ProfileEntry;
33 import org.simantics.scenegraph.profile.Style;
34 import org.simantics.scenegraph.profile.impl.DebugPolicy;
35 import org.simantics.scenegraph.profile.impl.ProfileActivationListener;
36 import org.simantics.scenegraph.profile.request.RuntimeProfileActiveEntries;
37 import org.simantics.utils.datastructures.Pair;
38 import org.simantics.utils.datastructures.disposable.IDisposable;
39 import org.simantics.utils.threads.IThreadWorkQueue;
40 import org.simantics.utils.threads.ThreadUtils;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 public class ProfileObserver implements EvaluationContext {
45
46     private static final Logger LOGGER = LoggerFactory.getLogger(ProfileObserver.class);
47
48     private final Session                     session;
49
50     /**
51      * Runtime diagram resource.
52      */
53     private final Resource                    resource;
54
55     private final IDisposable                 canvas;
56     private final IThreadWorkQueue            thread;
57     @SuppressWarnings("unused")
58     private final Object                      diagram;
59     private final Runnable                    notification;
60     private final G2DSceneGraph               sceneGraph;
61
62     private volatile boolean                  dirty               = true;
63     private volatile boolean                  disposed            = false;
64
65     private boolean                           needSynchronizedUpdates = false;
66     private List<Pair<Style, Object>>         updates             = new ArrayList<>();
67     private boolean                           updateAll;
68     
69     private ProfileActivationListener         activationListener;
70
71     private Map<String, Object>               constants           = new HashMap<String, Object>();
72
73     private Map<INode, Map<String, Object>>   temporaryProperties = new HashMap<INode, Map<String, Object>>();
74     private Map<INode, Map<String, Object>>   properties          = new HashMap<INode, Map<String, Object>>();
75
76     private final SessionEventListenerAdapter transactionListener = new SessionEventListenerAdapter() {
77         @Override
78         public void writeTransactionFinished() {
79             if (isDisposed())
80                 dispose();
81             if (dirty)
82                 perform();
83         }
84         @Override
85         public void readTransactionFinished() {
86             if (isDisposed())
87                 dispose();
88             if (dirty)
89                 perform();
90         }
91     };
92
93     public ProfileObserver(Session session, Resource resource, IThreadWorkQueue thread, IDisposable canvas, G2DSceneGraph sceneGraph, Object diagram, Map<String, Object> constants, Runnable notification) {
94         //System.out.println(this + " NEW PROFILE OBSERVER: ");
95         this.session = session;
96         this.resource = resource;
97         this.thread = thread;
98         this.canvas = canvas;
99         this.diagram = diagram;
100         this.sceneGraph = sceneGraph;
101         this.constants.putAll(constants);
102         this.notification = notification;
103         this.needSynchronizedUpdates = session.getService(QueryControl.class).getAmountOfQueryThreads() > 1;
104
105         attachSessionListener();
106
107         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
108             System.out.println("ProfileObserver(" + this + ")");
109         
110         // Tell SceneGraph that this observer is not yet done applying its operations
111         if(sceneGraph != null)
112             sceneGraph.setPending(ProfileObserver.this);
113         
114     }
115
116     private void attachSessionListener() {
117         SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);
118         eventSupport.addListener(transactionListener);
119     }
120
121     private void detachSessionListener() {
122         SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);
123         eventSupport.removeListener(transactionListener);
124     }
125
126     public void dispose() {
127         synchronized (this) {
128             if (disposed)
129                 return;
130             disposed = true;
131         }
132
133         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
134             System.out.println("ProfileObserver.dispose(" + this + ")");
135
136         if(activationListener != null) { 
137             activationListener.cleanup();
138             activationListener = null;
139         }
140
141         detachSessionListener();
142     }
143
144     @Override
145     public void update(Style style, Object item) {
146         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
147             System.out.println("Profile observer marked dirty.");
148
149         if (needSynchronizedUpdates) {
150             synchronized (updates) {
151                 updates.add(Pair.make(style, item));
152             }
153         } else {
154             updates.add(Pair.make(style, item));
155         }
156         //updateAll = true;
157         dirty = true;
158     }
159
160     public void update() {
161         updateAll = true;
162         dirty = true;
163     }
164     
165     private void perform() {
166         dirty = false;
167         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
168             System.out.println("Profile observer detected a change.");
169         
170         session.asyncRequest(new RuntimeProfileActiveEntries(resource), new Procedure<Collection<ProfileEntry>>() {
171             @Override
172             public void execute(final Collection<ProfileEntry> entries) {
173
174                 if (isDisposed())
175                     return;
176
177                 ThreadUtils.asyncExec(thread, new Runnable() {
178                     @Override
179                     public void run() {
180                         if (isDisposed())
181                             return;
182
183                         temporaryProperties.clear();
184
185                         long t0 = DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM ? System.nanoTime() : 0L;
186                         
187                         if (updateAll) {
188                             for(ProfileEntry e : entries) {
189                                 if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
190                                     System.out.println("Apply profile entry: " + e);
191                                 e.apply(ProfileObserver.this);
192                             }
193                             updateAll = false;
194                             if (needSynchronizedUpdates) {
195                                 synchronized (updates) {
196                                     updates.clear();
197                                 }
198                             } else {
199                                 updates.clear();
200                             }
201                         } else {
202                             List<Pair<Style, Object>> updatesCopy;
203                             if (needSynchronizedUpdates) {
204                                 synchronized (updates) {
205                                     updatesCopy = new ArrayList<>(updates);
206                                     updates.clear();
207                                 }
208                             } else {
209                                 updatesCopy = new ArrayList<>(updates);
210                                 updates.clear();
211                             }
212
213                             for (Pair<Style, Object> update : updatesCopy) {
214                                 Style style = update.first;
215                                 Object item = update.second;
216                                 
217                                 style.apply2(item, ProfileObserver.this);
218                             }
219                         }
220
221                         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM) {
222                             long t1 = System.nanoTime();
223                             System.out.println((t1-t0) / 1e6);
224                         }
225                         
226                         if(dirty) {
227                                 sceneGraph.setPending(ProfileObserver.this);
228 //                              System.err.println("setPending, dirty=true");
229                         }
230                         else {
231                                 sceneGraph.clearPending(ProfileObserver.this);
232 //                              System.err.println("clearPending, dirty=false");
233                         }
234                         
235                         notification.run();
236 //                        canvas.getContentContext().setDirty();
237                         
238                         // Something is underway, schedule update
239                         if (dirty) {
240                             Simantics.async(() -> {
241                                 if (isDisposed()) return;
242                                 if (dirty) perform();
243                             }, 100, TimeUnit.MILLISECONDS);
244                         }
245                     }
246                 });
247             }
248
249             @Override
250             public void exception(Throwable t) {
251                 LOGGER.error("RuntimeProfileActiveEntries request failed", t);
252             }
253         });
254     }
255
256     @Override
257     public boolean isDisposed() {
258         return disposed || canvas.isDisposed();
259     }
260
261     @Override
262     public void exception(Throwable throwable) {
263         LOGGER.error("Exception occurred during diagram profile observation", throwable);
264     }
265
266     @SuppressWarnings("unchecked")
267     @Override
268     public <T> T getTemporaryProperty(INode element, String key) {
269         Map<String, Object> map = temporaryProperties.get(element);
270         T t = map == null ? null : (T) map.get(key);
271         //System.out.println(this + ".getTemporaryProperty(" + element + ", " + key + "): " + t);
272         return t;
273     }
274
275     @Override
276     public <T> void setTemporaryProperty(INode element, String key, T value) {
277         //System.out.println(this + ".setTemporaryProperty(" + element + ", " + key + ", " + value + ")");
278         Map<String, Object> map = temporaryProperties.get(element);
279         if (map == null) {
280             if (value == null)
281                 return;
282             map = new HashMap<String, Object>(8);
283             temporaryProperties.put(element, map);
284         }
285         if (value == null) {
286             map.remove(key);
287             if (map.isEmpty())
288                 temporaryProperties.remove(element);
289         } else
290             map.put(key, value);
291     }
292
293     @SuppressWarnings("unchecked")
294     @Override
295     public <T> T getProperty(INode element, String key) {
296         Map<String, Object> map = properties.get(element);
297         T t = map == null ? null : (T) map.get(key);
298         //System.out.println(this + ".getProperty(" + element + ", " + key + "): " + t);
299         return t;
300     }
301
302     @SuppressWarnings("unchecked")
303     @Override
304     public <T> T setProperty(INode element, String key, T value) {
305         T result = null;
306         //System.out.println(this + ".setProperty(" + element + ", " + key + ", " + value + ")");
307         Map<String, Object> map = properties.get(element);
308         if (map == null) {
309             if (value == null)
310                 return null;
311             map = new HashMap<String, Object>(8);
312             properties.put(element, map);
313         }
314         if (value == null) {
315             result = (T) map.remove(key);
316             if (map.isEmpty())
317                 properties.remove(element);
318         } else
319             result = (T) map.put(key, value);
320         return result;
321     }
322
323     @SuppressWarnings("unchecked")
324     @Override
325     public <T> T getConstant(String key) {
326         return (T) constants.get(key);
327     }
328
329     @Override
330     public String toString() {
331         return "ProfileObserver[" + resource.getResourceId() + "]";
332     }
333
334 //    @Override
335 //    public ICanvasContext getContext() {
336 //      return canvas;
337 //    }
338 //    
339 //    @Override
340 //    public IDiagram getDiagram() {
341 //      return diagram;
342 //    }
343
344     public void listen(AsyncRequestProcessor processor, IDisposable disposable) {
345         activationListener = new ProfileActivationListener(resource, this, disposable);
346         processor.asyncRequest(new RuntimeProfileActiveEntries(resource), activationListener);
347     }
348
349     @Override
350     public Resource getResource() {
351         return resource;
352     }
353
354     @Override
355     public G2DSceneGraph getSceneGraph() {
356         return sceneGraph;
357     }
358
359 }