1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2011 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.ui.jobs;
\r
14 import java.util.function.Consumer;
\r
16 import org.eclipse.core.runtime.IProgressMonitor;
\r
17 import org.eclipse.core.runtime.IStatus;
\r
18 import org.eclipse.core.runtime.Status;
\r
19 import org.eclipse.core.runtime.jobs.Job;
\r
20 import org.simantics.DatabaseJob;
\r
21 import org.simantics.Simantics;
\r
22 import org.simantics.db.Session;
\r
23 import org.simantics.db.exception.DatabaseException;
\r
24 import org.simantics.db.layer0.util.SessionGarbageCollection;
\r
25 import org.simantics.db.service.LifecycleSupport;
\r
26 import org.simantics.utils.ui.ErrorLogger;
\r
29 * @author Tuukka Lehtonen
\r
31 public class SessionGarbageCollectorJob extends Job {
\r
33 private static SessionGarbageCollectorJob instance;
\r
35 public synchronized static SessionGarbageCollectorJob getInstance() {
\r
36 if (instance == null)
\r
37 instance = new SessionGarbageCollectorJob();
\r
41 private static final boolean TRACE = false;
\r
44 * At least 60 seconds between executions.
\r
46 private static final long DEFAULT_QUIET_TIME = 5000;
\r
49 private long quietTime;
\r
50 private long userDefinedQuietTime;
\r
52 private boolean enabled = true;
\r
57 public SessionGarbageCollectorJob() {
\r
58 this(DEFAULT_QUIET_TIME);
\r
62 * @param delayBetweenExecutions
\r
64 public SessionGarbageCollectorJob(long delayBetweenExecutions) {
\r
65 super("Database Garbage Collector");
\r
66 setPriority(Job.DECORATE);
\r
68 // Hide job from users.
\r
71 this.start = System.currentTimeMillis();
\r
72 this.quietTime = delayBetweenExecutions;
\r
73 this.userDefinedQuietTime = delayBetweenExecutions;
\r
77 * Cancels the currently scheduled execution of this job and reschedules
\r
78 * another execution after the current quiet time. This can be used for
\r
79 * easily pushing GC back a bit while performing an operation during which
\r
80 * it is not efficient to GC.
\r
82 * @see #setQuietTime(long)
\r
84 public void rescheduleAfterQuietTime() {
\r
86 scheduleAfterQuietTime();
\r
90 * Cancels the currently scheduled execution of this job and reschedules
\r
91 * another with no delay. This can be used for immediate forced execution of
\r
94 public void rescheduleNow() {
\r
103 public SessionGarbageCollectorJob setEnabled(boolean enabled) {
\r
104 this.enabled = enabled;
\r
109 // * @param quietTime quiet time between collections in
\r
112 // public SessionGarbageCollectorJob setQuietTime(long quietTime) {
\r
113 // this.quietTime = quietTime;
\r
118 * Schedules this job after the currently set quiet time.
\r
120 * @see #setQuietTime(long)
\r
122 public void scheduleAfterQuietTime() {
\r
123 schedule(quietTime);
\r
127 public boolean shouldSchedule() {
\r
132 public boolean shouldRun() {
\r
135 long t = System.currentTimeMillis();
\r
136 System.out.println("GC disabled, not running @ " + ((double) (t - start) * 1e-3) + " seconds");
\r
143 * Only invoked with actual errors, not <code>null</code> values.
\r
145 Consumer<DatabaseException> errorCallback = e -> ErrorLogger.defaultLogError(e);
\r
148 protected IStatus run(IProgressMonitor monitor) {
\r
149 long interval = quietTime;
\r
151 Session session = Simantics.peekSession();
\r
152 if (session == null)
\r
153 return Status.CANCEL_STATUS;
\r
154 LifecycleSupport lfs = session.peekService(LifecycleSupport.class);
\r
155 if (lfs == null || lfs.isClosed() || lfs.isClosing())
\r
156 return Status.CANCEL_STATUS;
\r
158 // Never run while a heavy database job is in progress.
\r
159 if (DatabaseJob.inProgress()) {
\r
160 // Schedule again in at most 10 seconds instead of
\r
161 // waiting for the whole quiet time period again.
\r
162 interval = Math.min(10, quietTime);
\r
163 return Status.CANCEL_STATUS;
\r
166 // // Don't run if there are currently write requests in progress
\r
167 // TransactionSupport ts = session.getService(TransactionSupport.class);
\r
168 // if (ts.getWriteCount() > 0) {
\r
170 // long t = System.currentTimeMillis();
\r
171 // System.out.println("Write in progress, no GC @ " + ((double) (t - start) * 1e-3) + " seconds");
\r
173 // return Status.CANCEL_STATUS;
\r
176 long begin = System.currentTimeMillis();
\r
178 System.out.println("running GC @ " + ((double) (begin - start) * 1e-3) + " seconds");
\r
181 boolean busy = SessionGarbageCollection.gc(monitor, session, true, errorCallback);
\r
183 quietTime = Math.max((3*quietTime)/4, 100);
\r
185 if(quietTime < userDefinedQuietTime)
\r
186 quietTime = Math.min(userDefinedQuietTime, (long)(1.2*quietTime));
\r
191 System.err.println("Session GC ended busy. New quiet time is " + quietTime);
\r
193 // This quiet time might have been adjusted in GC
\r
194 interval = quietTime;
\r
196 long intermediate = System.currentTimeMillis();
\r
198 // Clean up heap of all garbage left behind by SessionGarbageCollection
\r
202 //long end = System.currentTimeMillis();
\r
203 //System.out.println("Session.GC " + (intermediate - begin) + "ms, System.GC " + (end-intermediate) + "ms, total " + (end - begin) + "ms");
\r
204 System.out.println("Session.GC " + (intermediate - begin) + "ms");
\r
207 // Reschedule after a quiet period.
\r
208 return Status.OK_STATUS;
\r
210 schedule(interval);
\r