/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mapsforge.android.maps;
/**
* An abstract base class for threads which support pausing and resuming.
*/
public abstract class PausableThread extends Thread {
/**
* Specifies the scheduling priority of a {@link Thread}.
*/
protected enum ThreadPriority {
/**
* The priority between {@link #NORMAL} and {@link #HIGHEST}.
*/
ABOVE_NORMAL((Thread.NORM_PRIORITY + Thread.MAX_PRIORITY) / 2),
/**
* The priority between {@link #LOWEST} and {@link #NORMAL}.
*/
BELOW_NORMAL((Thread.NORM_PRIORITY + Thread.MIN_PRIORITY) / 2),
/**
* The maximum priority a thread can have.
*/
HIGHEST(MAX_PRIORITY),
/**
* The minimum priority a thread can have.
*/
LOWEST(MIN_PRIORITY),
/**
* The default priority of a thread.
*/
NORMAL(NORM_PRIORITY);
final int priority;
private ThreadPriority(int priority) {
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
throw new IllegalArgumentException("invalid priority: " + priority);
}
this.priority = priority;
}
}
private boolean pausing;
private boolean shouldPause;
/**
* Causes the current thread to wait until this thread is pausing.
*/
public final void awaitPausing() {
synchronized (this) {
while (!isInterrupted() && !isPausing()) {
try {
wait(100);
} catch (InterruptedException e) {
// restore the interrupted status
Thread.currentThread().interrupt();
}
}
}
}
@Override
public void interrupt() {
// first acquire the monitor which is used to call wait()
synchronized (this) {
super.interrupt();
}
}
/**
* @return true if this thread is currently pausing, false otherwise.
*/
public final synchronized boolean isPausing() {
return this.pausing;
}
/**
* The thread should stop its work temporarily.
*/
public final synchronized void pause() {
if (!this.shouldPause) {
this.shouldPause = true;
notify();
}
}
/**
* The paused thread should continue with its work.
*/
public final synchronized void proceed() {
if (this.shouldPause) {
this.shouldPause = false;
this.pausing = false;
afterPause();
notify();
}
}
@Override
public final void run() {
setName(getThreadName());
setPriority(getThreadPriority().priority);
while (!isInterrupted()) {
synchronized (this) {
while (!isInterrupted() && (this.shouldPause || !hasWork())) {
try {
if (this.shouldPause) {
this.pausing = true;
}
wait();
} catch (InterruptedException e) {
// restore the interrupted status
interrupt();
}
}
}
if (isInterrupted()) {
break;
}
try {
doWork();
} catch (InterruptedException e) {
// restore the interrupted status
interrupt();
}
}
afterRun();
}
/**
* Called once when this thread continues to work after a pause. The default implementation is empty.
*/
protected void afterPause() {
// do nothing
}
/**
* Called once at the end of the {@link #run()} method. The default implementation is empty.
*/
protected void afterRun() {
// do nothing
}
/**
* Called when this thread is not paused and should do its work.
*
* @throws InterruptedException
* if the thread has been interrupted.
*/
protected abstract void doWork() throws InterruptedException;
/**
* @return the name of this thread.
*/
protected abstract String getThreadName();
/**
* @return the priority which will be set for this thread.
*/
protected abstract ThreadPriority getThreadPriority();
/**
* @return true if this thread has some work to do, false otherwise.
*/
protected abstract boolean hasWork();
}