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