blob: 0bc9b5e19f6208a8d2281702514d7684701a1e22 [file] [log] [blame]
/*
* Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
* Please refer to the LICENSE.txt for licensing details.
*/
package ch.ethz.ssh2.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import ch.ethz.ssh2.log.Logger;
/**
* TimeoutService (beta). Here you can register a timeout.
* <p>
* Implemented having large scale programs in mind: if you open many concurrent SSH connections
* that rely on timeouts, then there will be only one timeout thread. Once all timeouts
* have expired/are cancelled, the thread will (sooner or later) exit.
* Only after new timeouts arrive a new thread (singleton) will be instantiated.
*
* @author Christian Plattner
* @version $Id: TimeoutService.java 41 2011-06-02 10:36:41Z dkocher@sudo.ch $
*/
public class TimeoutService
{
private static final Logger log = Logger.getLogger(TimeoutService.class);
public static class TimeoutToken
{
private long runTime;
private Runnable handler;
private TimeoutToken(long runTime, Runnable handler)
{
this.runTime = runTime;
this.handler = handler;
}
}
private static class TimeoutThread extends Thread
{
@Override
public void run()
{
synchronized (todolist)
{
while (true)
{
if (todolist.size() == 0)
{
timeoutThread = null;
return;
}
long now = System.currentTimeMillis();
TimeoutToken tt = (TimeoutToken) todolist.getFirst();
if (tt.runTime > now)
{
/* Not ready yet, sleep a little bit */
try
{
todolist.wait(tt.runTime - now);
}
catch (InterruptedException ignored)
{
}
/* We cannot simply go on, since it could be that the token
* was removed (cancelled) or another one has been inserted in
* the meantime.
*/
continue;
}
todolist.removeFirst();
try
{
tt.handler.run();
}
catch (Exception e)
{
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
log.warning("Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
}
}
}
}
}
/* The list object is also used for locking purposes */
private static final LinkedList<TimeoutToken> todolist = new LinkedList<TimeoutService.TimeoutToken>();
private static Thread timeoutThread = null;
/**
* It is assumed that the passed handler will not execute for a long time.
*
* @param runTime
* @param handler
* @return a TimeoutToken that can be used to cancel the timeout.
*/
public static TimeoutToken addTimeoutHandler(long runTime, Runnable handler)
{
TimeoutToken token = new TimeoutToken(runTime, handler);
synchronized (todolist)
{
todolist.add(token);
Collections.sort(todolist, new Comparator<TimeoutToken>()
{
public int compare(TimeoutToken o1, TimeoutToken o2)
{
if (o1.runTime > o2.runTime)
return 1;
if (o1.runTime == o2.runTime)
return 0;
return -1;
}
});
if (timeoutThread != null)
timeoutThread.interrupt();
else
{
timeoutThread = new TimeoutThread();
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}
return token;
}
public static void cancelTimeoutHandler(TimeoutToken token)
{
synchronized (todolist)
{
todolist.remove(token);
if (timeoutThread != null)
timeoutThread.interrupt();
}
}
}