• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
TimeoutToken(long runTime, Runnable handler)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
run()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 	 */
addTimeoutHandler(long runTime, Runnable handler)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 
cancelTimeoutHandler(TimeoutToken token)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