/**************************************************************************
* Copyright (c) 2009, 2011, 2014 by Chris Gray, KIFFER Ltd. *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of KIFFER Ltd nor the names of other contributors *
* may be used to endorse or promote products derived from this *
* software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL KIFFER LTD OR OTHER CONTRIBUTORS BE LIABLE FOR ANY *
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS *
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING *
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package java.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.PhantomReference;
public class Timer {
/**
* We use phantom references to track instances of
* java.util.Timer which have become unreachable.
* When the PhantomReference is enqueued we interrupt
* the TimerThread, which will see that its associated
* Timer is unreachable and so will terminate. Then
* GC can do its work and reclaim the whole caboodle.
* <p>We store the phantom references in a HashSet so
* that thsy themselves do not get garbage-collected.
*/
private static Set phantomReferences = new HashSet();
private static ReferenceQueue queue = new ReferenceQueue();
private static class TimerPhantomReference extends PhantomReference {
private TimerThread timerThread;
private TimerPhantomReference(Timer timer, ReferenceQueue queue, TimerThread timerThread) {
super(timer, queue);
this.timerThread = timerThread;
}
private void cleanup() {
timerThread.interrupt();
}
}
static {
/**
* This is the daemon thread which collects phantom
* references from the queue and interrupts the
* corresponding TimerThread.
*/
Runnable referenceGleaner = new Runnable() {
public void run() {
while (true) {
try {
TimerPhantomReference tpr = (TimerPhantomReference)queue.remove();
tpr.cleanup();
synchronized (phantomReferences) {
phantomReferences.remove(tpr);
}
} catch (Exception ex) {
// ignore
}
}
}
};
Thread gleanerThread = new Thread(referenceGleaner, "java.util.Timer clean-up thread");
gleanerThread.setDaemon(true);
gleanerThread.start();
}
private final TimerThread thread;
public Timer(){
this(false);
}
public Timer(boolean daemon){
thread = new TimerThread(this, daemon);
synchronized (phantomReferences) {
phantomReferences.add(new TimerPhantomReference(this, queue, thread));
}
}
public void cancel(){
if(!thread.cancelled){
synchronized(thread){
thread.cancelled = true;
if(thread.waiting){
thread.notifyAll();
}
}
}
}
public void schedule(TimerTask task, Date date){
thread.scheduleAtTime(task, date.getTime(), -1, false, true);
}
public void schedule(TimerTask task, Date date, long period){
if(period <= 0){
throw new IllegalArgumentException();
}
thread.scheduleAtTime(task, date.getTime(), period, false, true);
}
public void schedule(TimerTask task, long delay){
thread.schedule(task, delay, -1, false);
}
public void schedule(TimerTask task, long delay, long period){
if(period <= 0){
throw new IllegalArgumentException();
}
thread.schedule(task, delay, period, false);
}
public void scheduleAtFixedRate(TimerTask task, Date date, long period){
if(period <= 0){
throw new IllegalArgumentException();
}
thread.scheduleAtTime(task, date.getTime(), period, true, true);
}
public void scheduleAtFixedRate(TimerTask task, long delay, long period){
if(period <= 0){
throw new IllegalArgumentException();
}
thread.schedule(task, delay, period, true);
}
}