diff src/ch/ethz/ssh2/util/TimeoutService.java @ 273:91a31873c42a ganymed

start conversion from trilead to ganymed
author Carl Byington <carl@five-ten-sg.com>
date Fri, 18 Jul 2014 11:21:46 -0700
parents
children 071eccdff8ea
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/util/TimeoutService.java	Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,155 @@
+/*
+ * 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 89 2014-04-07 14:36:24Z 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 = 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();
+		}
+	}
+
+}