Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
      3  * Please refer to the LICENSE.txt for licensing details.
      4  */
      5 
      6 package ch.ethz.ssh2.util;
      7 
      8 import java.io.PrintWriter;
      9 import java.io.StringWriter;
     10 import java.util.Collections;
     11 import java.util.Comparator;
     12 import java.util.LinkedList;
     13 
     14 import ch.ethz.ssh2.log.Logger;
     15 
     16 /**
     17  * TimeoutService (beta). Here you can register a timeout.
     18  * <p>
     19  * Implemented having large scale programs in mind: if you open many concurrent SSH connections
     20  * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
     21  * have expired/are cancelled, the thread will (sooner or later) exit.
     22  * Only after new timeouts arrive a new thread (singleton) will be instantiated.
     23  *
     24  * @author Christian Plattner
     25  * @version $Id: TimeoutService.java 41 2011-06-02 10:36:41Z dkocher (at) sudo.ch $
     26  */
     27 public class TimeoutService
     28 {
     29 	private static final Logger log = Logger.getLogger(TimeoutService.class);
     30 
     31 	public static class TimeoutToken
     32 	{
     33 		private long runTime;
     34 		private Runnable handler;
     35 
     36 		private TimeoutToken(long runTime, Runnable handler)
     37 		{
     38 			this.runTime = runTime;
     39 			this.handler = handler;
     40 		}
     41 	}
     42 
     43 	private static class TimeoutThread extends Thread
     44 	{
     45 		@Override
     46 		public void run()
     47 		{
     48 			synchronized (todolist)
     49 			{
     50 				while (true)
     51 				{
     52 					if (todolist.size() == 0)
     53 					{
     54 						timeoutThread = null;
     55 						return;
     56 					}
     57 
     58 					long now = System.currentTimeMillis();
     59 
     60 					TimeoutToken tt = (TimeoutToken) todolist.getFirst();
     61 
     62 					if (tt.runTime > now)
     63 					{
     64 						/* Not ready yet, sleep a little bit */
     65 
     66 						try
     67 						{
     68 							todolist.wait(tt.runTime - now);
     69 						}
     70 						catch (InterruptedException ignored)
     71 						{
     72 						}
     73 
     74 						/* We cannot simply go on, since it could be that the token
     75 						 * was removed (cancelled) or another one has been inserted in
     76 						 * the meantime.
     77 						 */
     78 
     79 						continue;
     80 					}
     81 
     82 					todolist.removeFirst();
     83 
     84 					try
     85 					{
     86 						tt.handler.run();
     87 					}
     88 					catch (Exception e)
     89 					{
     90 						StringWriter sw = new StringWriter();
     91 						e.printStackTrace(new PrintWriter(sw));
     92 						log.warning("Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
     93 					}
     94 				}
     95 			}
     96 		}
     97 	}
     98 
     99 	/* The list object is also used for locking purposes */
    100 	private static final LinkedList<TimeoutToken> todolist = new LinkedList<TimeoutService.TimeoutToken>();
    101 
    102 	private static Thread timeoutThread = null;
    103 
    104 	/**
    105 	 * It is assumed that the passed handler will not execute for a long time.
    106 	 *
    107 	 * @param runTime
    108 	 * @param handler
    109 	 * @return a TimeoutToken that can be used to cancel the timeout.
    110 	 */
    111 	public static TimeoutToken addTimeoutHandler(long runTime, Runnable handler)
    112 	{
    113 		TimeoutToken token = new TimeoutToken(runTime, handler);
    114 
    115 		synchronized (todolist)
    116 		{
    117 			todolist.add(token);
    118 
    119 			Collections.sort(todolist, new Comparator<TimeoutToken>()
    120 			{
    121 				public int compare(TimeoutToken o1, TimeoutToken o2)
    122 				{
    123 					if (o1.runTime > o2.runTime)
    124 						return 1;
    125 					if (o1.runTime == o2.runTime)
    126 						return 0;
    127 					return -1;
    128 				}
    129 			});
    130 
    131 			if (timeoutThread != null)
    132 				timeoutThread.interrupt();
    133 			else
    134 			{
    135 				timeoutThread = new TimeoutThread();
    136 				timeoutThread.setDaemon(true);
    137 				timeoutThread.start();
    138 			}
    139 		}
    140 
    141 		return token;
    142 	}
    143 
    144 	public static void cancelTimeoutHandler(TimeoutToken token)
    145 	{
    146 		synchronized (todolist)
    147 		{
    148 			todolist.remove(token);
    149 
    150 			if (timeoutThread != null)
    151 				timeoutThread.interrupt();
    152 		}
    153 	}
    154 
    155 }
    156