You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

InterruptTimer.java 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * Copyright (C) 2009, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.util.io;
  44. /**
  45. * Triggers an interrupt on the calling thread if it doesn't complete a block.
  46. * <p>
  47. * Classes can use this to trip an alarm interrupting the calling thread if it
  48. * doesn't complete a block within the specified timeout. Typical calling
  49. * pattern is:
  50. *
  51. * <pre>
  52. * private InterruptTimer myTimer = ...;
  53. * void foo() {
  54. * try {
  55. * myTimer.begin(timeout);
  56. * // work
  57. * } finally {
  58. * myTimer.end();
  59. * }
  60. * }
  61. * </pre>
  62. * <p>
  63. * An InterruptTimer is not recursive. To implement recursive timers,
  64. * independent InterruptTimer instances are required. A single InterruptTimer
  65. * may be shared between objects which won't recursively call each other.
  66. * <p>
  67. * Each InterruptTimer spawns one background thread to sleep the specified time
  68. * and interrupt the thread which called {@link #begin(int)}. It is up to the
  69. * caller to ensure that the operations within the work block between the
  70. * matched begin and end calls tests the interrupt flag (most IO operations do).
  71. * <p>
  72. * To terminate the background thread, use {@link #terminate()}. If the
  73. * application fails to terminate the thread, it will (eventually) terminate
  74. * itself when the InterruptTimer instance is garbage collected.
  75. *
  76. * @see TimeoutInputStream
  77. */
  78. public final class InterruptTimer {
  79. private final AlarmState state;
  80. private final AlarmThread thread;
  81. final AutoKiller autoKiller;
  82. /** Create a new timer with a default thread name. */
  83. public InterruptTimer() {
  84. this("JGit-InterruptTimer");
  85. }
  86. /**
  87. * Create a new timer to signal on interrupt on the caller.
  88. * <p>
  89. * The timer thread is created in the calling thread's ThreadGroup.
  90. *
  91. * @param threadName
  92. * name of the timer thread.
  93. */
  94. public InterruptTimer(final String threadName) {
  95. state = new AlarmState();
  96. autoKiller = new AutoKiller(state);
  97. thread = new AlarmThread(threadName, state);
  98. thread.start();
  99. }
  100. /**
  101. * Arm the interrupt timer before entering a blocking operation.
  102. *
  103. * @param timeout
  104. * number of milliseconds before the interrupt should trigger.
  105. * Must be > 0.
  106. */
  107. public void begin(final int timeout) {
  108. if (timeout <= 0)
  109. throw new IllegalArgumentException("Invalid timeout: " + timeout);
  110. Thread.interrupted();
  111. state.begin(timeout);
  112. }
  113. /** Disable the interrupt timer, as the operation is complete. */
  114. public void end() {
  115. state.end();
  116. }
  117. /** Shutdown the timer thread, and wait for it to terminate. */
  118. public void terminate() {
  119. state.terminate();
  120. try {
  121. thread.join();
  122. } catch (InterruptedException e) {
  123. //
  124. }
  125. }
  126. static final class AlarmThread extends Thread {
  127. AlarmThread(final String name, final AlarmState q) {
  128. super(q);
  129. setName(name);
  130. setDaemon(true);
  131. }
  132. }
  133. // The trick here is, the AlarmThread does not have a reference to the
  134. // AutoKiller instance, only the InterruptTimer itself does. Thus when
  135. // the InterruptTimer is GC'd, the AutoKiller is also unreachable and
  136. // can be GC'd. When it gets finalized, it tells the AlarmThread to
  137. // terminate, triggering the thread to exit gracefully.
  138. //
  139. private static final class AutoKiller {
  140. private final AlarmState state;
  141. AutoKiller(final AlarmState s) {
  142. state = s;
  143. }
  144. @Override
  145. protected void finalize() throws Throwable {
  146. state.terminate();
  147. }
  148. }
  149. static final class AlarmState implements Runnable {
  150. private Thread callingThread;
  151. private long deadline;
  152. private boolean terminated;
  153. AlarmState() {
  154. callingThread = Thread.currentThread();
  155. }
  156. public synchronized void run() {
  157. while (!terminated && callingThread.isAlive()) {
  158. try {
  159. if (0 < deadline) {
  160. final long delay = deadline - now();
  161. if (delay <= 0) {
  162. deadline = 0;
  163. callingThread.interrupt();
  164. } else {
  165. wait(delay);
  166. }
  167. } else {
  168. wait(1000);
  169. }
  170. } catch (InterruptedException e) {
  171. // Treat an interrupt as notice to examine state.
  172. }
  173. }
  174. }
  175. synchronized void begin(final int timeout) {
  176. if (terminated)
  177. throw new IllegalStateException("Timer already terminated");
  178. callingThread = Thread.currentThread();
  179. deadline = now() + timeout;
  180. notifyAll();
  181. }
  182. synchronized void end() {
  183. if (0 == deadline)
  184. Thread.interrupted();
  185. else
  186. deadline = 0;
  187. notifyAll();
  188. }
  189. synchronized void terminate() {
  190. if (!terminated) {
  191. deadline = 0;
  192. terminated = true;
  193. notifyAll();
  194. }
  195. }
  196. private static long now() {
  197. return System.currentTimeMillis();
  198. }
  199. }
  200. }