Skip to content

Instantly share code, notes, and snippets.

@halfhp
Last active October 13, 2016 22:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save halfhp/5e746eb45552524b8cc22a270011ca3b to your computer and use it in GitHub Desktop.
Save halfhp/5e746eb45552524b8cc22a270011ca3b to your computer and use it in GitHub Desktop.
Utility for preventing duplicate attempts to run background logic due to quickly pressing a button, etc.
/**
* Utility for preventing duplicate attempts to run background logic due to quickly pressing a button, etc.
* Similar to {@link Throttle} but automatically unlocks after callback completion instead of on throttle timeout ellapsing.
*
* Usage:
*
* BusyLock.obtain(new Callback() {
* public void run() {
* doSomethingInTheBackground();
* displayASuccessDialogOrWhatever();
* }
* });
*/
public class BusyLock {
/**
* A nominal amount of delay is necessary to block input between the time that the background op etc.
* completes and when it's subsequent invocation of a dialog display op is taken from the task queue
* and executed.
*/
private static final long COOLDOWN_MS = 100;
private boolean isBusy = false;
private static final BusyLock busyLock = new BusyLock();
public interface Callback {
void run();
}
/**
* Same as {@link #obtainAndRun(Callback)} except operates on a global BusyLock instance. If for some reason you need
* to only serialize the invocation of a single process, create a separate BusyLock instance and use
* {@link #obtainAndRun(Callback)}.
* @param callback
*/
public static void obtain(Callback callback) {
busyLock.obtainAndRun(callback);
}
/**
* Attempts to obtain the busy lock. If successful, the callback will be invoked, otherwise
* no action will be taken.
* @param callback
*/
public void obtainAndRun(Callback callback) {
if(!isBusy) {
try {
isBusy = true;
callback.run();
} finally {
unlockWithCooldown();
}
}
}
/**
* Manually unlock the BusyLock. This is generally not necessary as BusyLock will automatically
* unlock it's self after cooldown, however if a cooldown is not needed/wanted, invoking this
* method at the end of the callback logic will disable the cooldown for that invocation.
*/
public void unlock() {
this.isBusy = false;
}
protected void unlockWithCooldown() {
if(isBusy) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(COOLDOWN_MS);
} catch (InterruptedException e) {
// if something goes wrong the finally block gets invoked
// immediately and isBusy becomes false.
} finally {
isBusy = false;
}
}
}).start();
}
}
public boolean isLocked() {
return isBusy;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment