]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.ui/src/org/simantics/ui/jobs/SessionGarbageCollectorJob.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / jobs / SessionGarbageCollectorJob.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2011 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.ui.jobs;
13
14 import java.util.function.Consumer;
15
16 import org.eclipse.core.runtime.IProgressMonitor;
17 import org.eclipse.core.runtime.IStatus;
18 import org.eclipse.core.runtime.Status;
19 import org.eclipse.core.runtime.jobs.Job;
20 import org.simantics.DatabaseJob;
21 import org.simantics.Simantics;
22 import org.simantics.db.Session;
23 import org.simantics.db.exception.DatabaseException;
24 import org.simantics.db.layer0.util.SessionGarbageCollection;
25 import org.simantics.db.service.LifecycleSupport;
26 import org.simantics.utils.ui.ErrorLogger;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * @author Tuukka Lehtonen
32  */
33 public class SessionGarbageCollectorJob extends Job {
34
35     private static final Logger LOGGER = LoggerFactory.getLogger(SessionGarbageCollectorJob.class);
36     private static SessionGarbageCollectorJob instance;
37
38     public synchronized static SessionGarbageCollectorJob getInstance() {
39         if (instance == null)
40             instance = new SessionGarbageCollectorJob();
41         return instance;
42     }
43
44     private static final boolean TRACE                  = false;
45
46     /**
47      * At least 60 seconds between executions.
48      */
49     private static final long    DEFAULT_QUIET_TIME     = 5000;
50
51     private long                 start;
52     private long                 quietTime;
53     private long                 userDefinedQuietTime;
54
55     private boolean              enabled                = true;
56
57     /**
58      * 
59      */
60     public SessionGarbageCollectorJob() {
61         this(DEFAULT_QUIET_TIME);
62     }
63
64     /**
65      * @param delayBetweenExecutions
66      */
67     public SessionGarbageCollectorJob(long delayBetweenExecutions) {
68         super("Database Garbage Collector");
69         setPriority(Job.DECORATE);
70
71         // Hide job from users.
72         setSystem(true);
73
74         this.start = System.currentTimeMillis();
75         this.quietTime = delayBetweenExecutions;
76         this.userDefinedQuietTime = delayBetweenExecutions;
77     }
78
79     /**
80      * Cancels the currently scheduled execution of this job and reschedules
81      * another execution after the current quiet time. This can be used for
82      * easily pushing GC back a bit while performing an operation during which
83      * it is not efficient to GC.
84      *
85      * @see #setQuietTime(long)
86      */
87     public void rescheduleAfterQuietTime() {
88         cancel();
89         scheduleAfterQuietTime();
90     }
91
92     /**
93      * Cancels the currently scheduled execution of this job and reschedules
94      * another with no delay. This can be used for immediate forced execution of
95      * session GC.
96      */
97     public void rescheduleNow() {
98         cancel();
99         schedule();
100     }
101
102     /**
103      * @param enabled
104      * @return
105      */
106     public SessionGarbageCollectorJob setEnabled(boolean enabled) {
107         this.enabled = enabled;
108         return this;
109     }
110
111 //    /**
112 //     * @param quietTime quiet time between collections in
113 //     *        milliseconds
114 //     */
115 //    public SessionGarbageCollectorJob setQuietTime(long quietTime) {
116 //        this.quietTime = quietTime;
117 //        return this;
118 //    }
119
120     /**
121      * Schedules this job after the currently set quiet time.
122      * 
123      * @see #setQuietTime(long)
124      */
125     public void scheduleAfterQuietTime() {
126         schedule(quietTime);
127     }
128
129     @Override
130     public boolean shouldSchedule() {
131         return enabled;
132     }
133
134     @Override
135     public boolean shouldRun() {
136         if (TRACE) {
137             if (!enabled) {
138                 long t = System.currentTimeMillis();
139                 LOGGER.info("GC disabled, not running @ " + ((double) (t - start) * 1e-3) + " seconds");
140             }
141         }
142         return enabled;
143     }
144
145     /**
146      * Only invoked with actual errors, not <code>null</code> values.
147      */
148     Consumer<DatabaseException> errorCallback = e -> ErrorLogger.defaultLogError(e);
149
150     @Override
151     protected IStatus run(IProgressMonitor monitor) {
152         long interval = quietTime;
153         try {
154             Session session = Simantics.peekSession();
155             if (session == null)
156                 return Status.CANCEL_STATUS;
157             LifecycleSupport lfs = session.peekService(LifecycleSupport.class);
158             if (lfs == null || lfs.isClosed() || lfs.isClosing())
159                 return Status.CANCEL_STATUS;
160
161             // Never run while a heavy database job is in progress.
162             if (DatabaseJob.inProgress()) {
163                 // Schedule again in at most 10 seconds instead of
164                 // waiting for the whole quiet time period again.
165                 interval = Math.min(10, quietTime);
166                 return Status.CANCEL_STATUS;
167             }
168
169 //            // Don't run if there are currently write requests in progress
170 //            TransactionSupport ts = session.getService(TransactionSupport.class);
171 //            if (ts.getWriteCount() > 0) {
172 //                if (TRACE) {
173 //                    long t = System.currentTimeMillis();
174 //                    System.out.println("Write in progress, no GC @ " + ((double) (t - start) * 1e-3) + " seconds");
175 //                }
176 //                return Status.CANCEL_STATUS;
177 //            }
178
179             long begin = System.currentTimeMillis();
180             if (TRACE) {
181                 LOGGER.info("running GC @ " + ((double) (begin - start) * 1e-3) + " seconds");
182             }
183
184             boolean busy = SessionGarbageCollection.gc(monitor, session, true, errorCallback);
185             if(busy) {
186                 quietTime = Math.max((3*quietTime)/4, 100);
187             } else {
188                 if(quietTime < userDefinedQuietTime)
189                         quietTime = Math.min(userDefinedQuietTime, (long)(1.2*quietTime));
190             }
191
192             if (TRACE)
193                 if(busy)
194                         LOGGER.info("Session GC ended busy. New quiet time is " + quietTime);
195             
196             // This quiet time might have been adjusted in GC
197             interval = quietTime;
198
199             long intermediate = System.currentTimeMillis();
200
201             // Clean up heap of all garbage left behind by SessionGarbageCollection 
202             //System.gc();
203
204             if (TRACE) {
205                 //long end = System.currentTimeMillis();
206                 //System.out.println("Session.GC " + (intermediate - begin) + "ms, System.GC " + (end-intermediate) + "ms, total " + (end - begin) + "ms");
207                 LOGGER.info("Session.GC " + (intermediate - begin) + "ms");
208             }
209
210             // Reschedule after a quiet period.
211             return Status.OK_STATUS;
212         } finally {
213             schedule(interval);
214             monitor.done();
215         }
216     }
217
218 }