Skip to content

Commit

Permalink
Eliminating hot lock from TimeoutService (#35)
Browse files Browse the repository at this point in the history
* Removing hot lock by using a scheduler instead.

* Making the threads Daemon threads.
  • Loading branch information
Stefan Zier authored and kuisathaverat committed Mar 31, 2019
1 parent 986fd2a commit f880cf7
Showing 1 changed file with 61 additions and 132 deletions.
193 changes: 61 additions & 132 deletions src/com/trilead/ssh2/util/TimeoutService.java
Original file line number Diff line number Diff line change
@@ -1,147 +1,76 @@

package com.trilead.ssh2.util;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.LinkedList;

import com.trilead.ssh2.log.Logger;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


/**
* TimeoutService (beta). Here you can register a timeout.
* <p>
* <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, plattner@trilead.com
* @version $Id: TimeoutService.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class TimeoutService
{
private static final Logger log = Logger.getLogger(TimeoutService.class);

public static class TimeoutToken implements Comparable
{
private long runTime;
private Runnable handler;

private TimeoutToken(long runTime, Runnable handler)
{
this.runTime = runTime;
this.handler = handler;
}

public int compareTo(Object o)
{
TimeoutToken t = (TimeoutToken) o;
if (runTime > t.runTime)
return 1;
if (runTime == t.runTime)
return 0;
return -1;
}
}

private static class TimeoutThread extends Thread
{
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 e)
{
}

/* 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)
{
log.log(20, "Exception in Timeout handler",e);
}
}
}
}
}

/* The list object is also used for locking purposes */
private static final LinkedList todolist = new LinkedList();

private static Thread timeoutThread = null;

/**
* It is assumed that the passed handler will not execute for a long time.
*
* @param runTime runTime
* @param handler handler
* @return a TimeoutToken that can be used to cancel the timeout.
*/
public static final TimeoutToken addTimeoutHandler(long runTime, Runnable handler)
{
TimeoutToken token = new TimeoutToken(runTime, handler);

synchronized (todolist)
{
todolist.add(token);
Collections.sort(todolist);

if (timeoutThread != null)
timeoutThread.interrupt();
else
{
timeoutThread = new TimeoutThread();
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}

return token;
}

public static final void cancelTimeoutHandler(TimeoutToken token)
{
synchronized (todolist)
{
todolist.remove(token);

if (timeoutThread != null)
timeoutThread.interrupt();
}
}

public class TimeoutService {

private final static ThreadFactory threadFactory = new ThreadFactory() {

private AtomicInteger count = new AtomicInteger();

public Thread newThread(Runnable r) {
int threadNumber = count.incrementAndGet();
String threadName = "TimeoutService-" + threadNumber;
Thread thread = new Thread(r, threadName);
thread.setDaemon(true);
return thread;
}
};

private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(20, threadFactory);

public static class TimeoutToken implements Runnable {
private Runnable handler;
private boolean cancelled = false;

public void run() {
if (!cancelled) {
handler.run();
}
}
}

/**
* It is assumed that the passed handler will not execute for a long time.
*
* @param runTime runTime
* @param handler handler
* @return a TimeoutToken that can be used to cancel the timeout.
*/
public static final TimeoutToken addTimeoutHandler(long runTime, Runnable handler) {
TimeoutToken token = new TimeoutToken();
token.handler = handler;
long delay = runTime - System.currentTimeMillis();
if (delay < 0) {
delay = 0;
}
scheduler.schedule(token, delay, TimeUnit.MILLISECONDS);
return token;
}

/**
* Cancel the timeout callback for the specified token.
*
* @param token
*/
public static final void cancelTimeoutHandler(TimeoutToken token) {
token.cancelled = true;
}
}

0 comments on commit f880cf7

Please sign in to comment.