1. /*
  2. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. /*
  6. * @(#)AbstractInterruptibleChannel.java 1.15 03/12/19
  7. */
  8. package java.nio.channels.spi;
  9. import java.io.IOException;
  10. import java.lang.reflect.Method;
  11. import java.lang.reflect.InvocationTargetException;
  12. import java.nio.channels.*;
  13. import java.security.AccessController;
  14. import java.security.PrivilegedAction;
  15. import sun.nio.ch.Interruptible;
  16. /**
  17. * Base implementation class for interruptible channels.
  18. *
  19. * <p> This class encapsulates the low-level machinery required to implement
  20. * the asynchronous closing and interruption of channels. A concrete channel
  21. * class must invoke the {@link #begin begin} and {@link #end end} methods
  22. * before and after, respectively, invoking an I/O operation that might block
  23. * indefinitely. In order to ensure that the {@link #end end} method is always
  24. * invoked, these methods should be used within a
  25. * <tt>try</tt> ... <tt>finally</tt> block: <a name="be">
  26. *
  27. * <blockquote><pre>
  28. * boolean completed = false;
  29. * try {
  30. * begin();
  31. * completed = ...; // Perform blocking I/O operation
  32. * return ...; // Return result
  33. * } finally {
  34. * end(completed);
  35. * }</pre></blockquote>
  36. *
  37. * <p> The <tt>completed</tt> argument to the {@link #end end} method tells
  38. * whether or not the I/O operation actually completed, that is, whether it had
  39. * any effect that would be visible to the invoker. In the case of an
  40. * operation that reads bytes, for example, this argument should be
  41. * <tt>true</tt> if, and only if, some bytes were actually transferred into the
  42. * invoker's target buffer.
  43. *
  44. * <p> A concrete channel class must also implement the {@link
  45. * #implCloseChannel implCloseChannel} method in such a way that if it is
  46. * invoked while another thread is blocked in a native I/O operation upon the
  47. * channel then that operation will immediately return, either by throwing an
  48. * exception or by returning normally. If a thread is interrupted or the
  49. * channel upon which it is blocked is asynchronously closed then the channel's
  50. * {@link #end end} method will throw the appropriate exception.
  51. *
  52. * <p> This class performs the synchronization required to implement the {@link
  53. * java.nio.channels.Channel} specification. Implementations of the {@link
  54. * #implCloseChannel implCloseChannel} method need not synchronize against
  55. * other threads that might be attempting to close the channel. </p>
  56. *
  57. *
  58. * @author Mark Reinhold
  59. * @author JSR-51 Expert Group
  60. * @version 1.15, 03/12/19
  61. * @since 1.4
  62. */
  63. public abstract class AbstractInterruptibleChannel
  64. implements Channel, InterruptibleChannel
  65. {
  66. private Object closeLock = new Object();
  67. private volatile boolean open = true;
  68. /**
  69. * Initializes a new instance of this class.
  70. */
  71. protected AbstractInterruptibleChannel() { }
  72. /**
  73. * Closes this channel.
  74. *
  75. * <p> If the channel has already been closed then this method returns
  76. * immediately. Otherwise it marks the channel as closed and then invokes
  77. * the {@link #implCloseChannel implCloseChannel} method in order to
  78. * complete the close operation. </p>
  79. *
  80. * @throws IOException
  81. * If an I/O error occurs
  82. */
  83. public final void close() throws IOException {
  84. synchronized (closeLock) {
  85. if (!open)
  86. return;
  87. open = false;
  88. implCloseChannel();
  89. }
  90. }
  91. /**
  92. * Closes this channel.
  93. *
  94. * <p> This method is invoked by the {@link #close close} method in order
  95. * to perform the actual work of closing the channel. This method is only
  96. * invoked if the channel has not yet been closed, and it is never invoked
  97. * more than once.
  98. *
  99. * <p> An implementation of this method must arrange for any other thread
  100. * that is blocked in an I/O operation upon this channel to return
  101. * immediately, either by throwing an exception or by returning normally.
  102. * </p>
  103. *
  104. * @throws IOException
  105. * If an I/O error occurs while closing the channel
  106. */
  107. protected abstract void implCloseChannel() throws IOException;
  108. public final boolean isOpen() {
  109. return open;
  110. }
  111. // -- Interruption machinery --
  112. private Interruptible interruptor;
  113. private volatile boolean interrupted = false;
  114. /**
  115. * Marks the beginning of an I/O operation that might block indefinitely.
  116. *
  117. * <p> This method should be invoked in tandem with the {@link #end end}
  118. * method, using a <tt>try</tt> ... <tt>finally</tt> block as
  119. * shown <a href="#be">above</a>, in order to implement asynchronous
  120. * closing and interruption for this channel. </p>
  121. */
  122. protected final void begin() {
  123. if (interruptor == null) {
  124. interruptor = new Interruptible() {
  125. public void interrupt() {
  126. synchronized (closeLock) {
  127. if (!open)
  128. return;
  129. interrupted = true;
  130. open = false;
  131. try {
  132. AbstractInterruptibleChannel.this.implCloseChannel();
  133. } catch (IOException x) { }
  134. }
  135. }};
  136. }
  137. blockedOn(interruptor);
  138. if (Thread.currentThread().isInterrupted())
  139. interruptor.interrupt();
  140. }
  141. /**
  142. * Marks the end of an I/O operation that might block indefinitely.
  143. *
  144. * <p> This method should be invoked in tandem with the {@link #begin
  145. * begin} method, using a <tt>try</tt> ... <tt>finally</tt> block
  146. * as shown <a href="#be">above</a>, in order to implement asynchronous
  147. * closing and interruption for this channel. </p>
  148. *
  149. * @param completed
  150. * <tt>true</tt> if, and only if, the I/O operation completed
  151. * successfully, that is, had some effect that would be visible to
  152. * the operation's invoker
  153. *
  154. * @throws AsynchronousCloseException
  155. * If the channel was asynchronously closed
  156. *
  157. * @throws ClosedByInterruptException
  158. * If the thread blocked in the I/O operation was interrupted
  159. */
  160. protected final void end(boolean completed)
  161. throws AsynchronousCloseException
  162. {
  163. blockedOn(null);
  164. if (completed) {
  165. interrupted = false;
  166. return;
  167. }
  168. if (interrupted) throw new ClosedByInterruptException();
  169. if (!open) throw new AsynchronousCloseException();
  170. }
  171. // -- Reflection hackery --
  172. private static Method blockedOnMethod = null;
  173. static void blockedOn(Interruptible intr) { // package-private
  174. if (blockedOnMethod == null)
  175. initBlockedOn();
  176. try {
  177. blockedOnMethod.invoke(Thread.currentThread(),
  178. new Object[] { intr });
  179. } catch (IllegalAccessException x) {
  180. throw new Error(x);
  181. } catch (IllegalArgumentException x) {
  182. throw new Error(x);
  183. } catch (InvocationTargetException x) {
  184. throw new Error(x);
  185. }
  186. }
  187. private static void initBlockedOn() {
  188. AccessController.doPrivileged(new PrivilegedAction() {
  189. public Object run() {
  190. try {
  191. Class th = Class.forName("java.lang.Thread");
  192. blockedOnMethod
  193. = th.getDeclaredMethod("blockedOn",
  194. new Class[] { Interruptible.class });
  195. blockedOnMethod.setAccessible(true);
  196. } catch (ClassNotFoundException x) {
  197. throw new Error(x);
  198. } catch (NoSuchMethodException x) {
  199. throw new Error(x);
  200. } catch (IllegalArgumentException x) {
  201. throw new Error(x);
  202. } catch (ClassCastException x) {
  203. throw new Error(x);
  204. }
  205. return null;
  206. }});
  207. }
  208. // Workaround for apparent VM bug: Sometimes an interrupted thread
  209. // cannot load a class
  210. private static class FooChannel extends AbstractInterruptibleChannel {
  211. protected void implCloseChannel() { }
  212. }
  213. static {
  214. FooChannel fc = new FooChannel();
  215. fc.begin();
  216. try {
  217. fc.end(true);
  218. } catch (IOException e) { }
  219. }
  220. }