/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *******************************************************************************/ package org.andork.util; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * Schedules background save tasks for automatic persistence, so that they * happen within a given delay time, but no more often than the given delay * time. * * @author andy.edwards */ public class PersistenceScheduler { private long delay = 100; private Object lock = new Object(); private Map<Object, Runnable> dirtyMap = new HashMap<Object, Runnable>(); private Set<Object> runningSet = new HashSet<Object>(); /** * Schedules a save task on the given executor thread. If no task for the * given dirty object is currently running, the given task will be run as * soon as possible. Otherwise, it will be run within {@link #delay} * milliseconds after the current task finishes. * * @param executor * the {@link ScheduledExecutorService} to submit the tasks to. * @param dirtyObj * a unique {@link HashMap} key for the object that needs to be * saved. * @param saver * the {@link Runnable} that will perform the background save * task. */ public void save(final ScheduledExecutorService executor, final Object dirtyObj, final Runnable saver) { boolean submit = false; synchronized (lock) { dirtyMap.put(dirtyObj, saver); submit = runningSet.add(dirtyObj); } if (submit) { executor.submit(new Runnable() { @Override public void run() { Runnable saver; synchronized (lock) { saver = dirtyMap.remove(dirtyObj); } if (saver != null) { saver.run(); } boolean schedule = false; synchronized (lock) { if (dirtyMap.containsKey(dirtyObj)) { schedule = true; } else { runningSet.remove(dirtyObj); } } if (schedule) { executor.schedule(this, delay, TimeUnit.MILLISECONDS); } } }); } } /** * Sets the time to wait between save tasks on the same dirty object. * * @param delay * the delay, in milliseconds */ public void setDelay(long delay) { if (delay < 0) { throw new IllegalArgumentException("delay must be >= 0"); } this.delay = delay; } }