1. /*
  2. * @(#)AbstractSelectableChannel.java 1.21 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.nio.channels.spi;
  8. import java.io.IOException;
  9. import java.nio.channels.*;
  10. /**
  11. * Base implementation class for selectable channels.
  12. *
  13. * <p> This class defines methods that handle the mechanics of channel
  14. * registration, deregistration, and closing. It maintains the current
  15. * blocking mode of this channel as well as its current set of selection keys.
  16. * It performs all of the synchronization required to implement the {@link
  17. * java.nio.channels.SelectableChannel} specification. Implementations of the
  18. * abstract protected methods defined in this class need not synchronize
  19. * against other threads that might be engaged in the same operations. </p>
  20. *
  21. *
  22. * @author Mark Reinhold
  23. * @author Mike McCloskey
  24. * @author JSR-51 Expert Group
  25. * @version 1.21, 03/01/23
  26. * @since 1.4
  27. */
  28. public abstract class AbstractSelectableChannel
  29. extends SelectableChannel
  30. {
  31. // The provider that created this channel
  32. private final SelectorProvider provider;
  33. // Keys that have been created by registering this channel with selectors.
  34. // They are saved because if this channel is closed the keys must be
  35. // deregistered. Protected by keyLock.
  36. //
  37. private SelectionKey[] keys = null;
  38. private int keyCount = 0;
  39. // Lock for key set and count
  40. private final Object keyLock = new Object();
  41. // Lock for registration and configureBlocking operations
  42. private final Object regLock = new Object();
  43. // Blocking mode, protected by regLock
  44. boolean blocking = true;
  45. /**
  46. * Initializes a new instance of this class.
  47. */
  48. protected AbstractSelectableChannel(SelectorProvider provider) {
  49. this.provider = provider;
  50. }
  51. /**
  52. * Returns the provider that created this channel.
  53. *
  54. * @return The provider that created this channel
  55. */
  56. public final SelectorProvider provider() {
  57. return provider;
  58. }
  59. // -- Utility methods for the key set --
  60. private void addKey(SelectionKey k) {
  61. synchronized (keyLock) {
  62. int i = 0;
  63. if ((keys != null) && (keyCount < keys.length)) {
  64. // Find empty element of key array
  65. for (i = 0; i < keys.length; i++)
  66. if (keys[i] == null)
  67. break;
  68. } else if (keys == null) {
  69. keys = new SelectionKey[3];
  70. } else {
  71. // Grow key array
  72. int n = keys.length * 2;
  73. SelectionKey[] ks = new SelectionKey[n];
  74. for (i = 0; i < keys.length; i++)
  75. ks[i] = keys[i];
  76. keys = ks;
  77. i = keyCount;
  78. }
  79. keys[i] = k;
  80. keyCount++;
  81. }
  82. }
  83. private SelectionKey findKey(Selector sel) {
  84. synchronized (keyLock) {
  85. if (keys == null)
  86. return null;
  87. for (int i = 0; i < keys.length; i++)
  88. if ((keys[i] != null) && (keys[i].selector() == sel))
  89. return keys[i];
  90. return null;
  91. }
  92. }
  93. void removeKey(SelectionKey k) { // package-private
  94. synchronized (keyLock) {
  95. int i;
  96. for (i = 0; i < keys.length; i++)
  97. if (keys[i] == k) {
  98. keys[i] = null;
  99. keyCount--;
  100. }
  101. assert (i <= keyCount);
  102. }
  103. }
  104. private boolean haveValidKeys() {
  105. synchronized (keyLock) {
  106. if (keyCount == 0)
  107. return false;
  108. for (int i = 0; i < keys.length; i++) {
  109. if ((keys[i] != null) && keys[i].isValid())
  110. return true;
  111. }
  112. return false;
  113. }
  114. }
  115. // -- Registration --
  116. public final boolean isRegistered() {
  117. synchronized (keyLock) {
  118. return keyCount != 0;
  119. }
  120. }
  121. public final SelectionKey keyFor(Selector sel) {
  122. return findKey(sel);
  123. }
  124. /**
  125. * Registers this channel with the given selector, returning a selection key.
  126. *
  127. * <p> This method first verifies that this channel is open and that the
  128. * given initial interest set is valid.
  129. *
  130. * <p> If this channel is already registered with the given selector then
  131. * the selection key representing that registration is returned after
  132. * setting its interest set to the given value.
  133. *
  134. * <p> Otherwise this channel has not yet been registered with the given
  135. * selector, so the {@link AbstractSelector#register register} method of
  136. * the selector is invoked while holding the appropriate locks. The
  137. * resulting key is added to this channel's key set before being returned.
  138. * </p>
  139. */
  140. public final SelectionKey register(Selector sel, int ops,
  141. Object att)
  142. throws ClosedChannelException
  143. {
  144. if (!isOpen())
  145. throw new ClosedChannelException();
  146. if ((ops & ~validOps()) != 0)
  147. throw new IllegalArgumentException();
  148. synchronized (regLock) {
  149. if (blocking)
  150. throw new IllegalBlockingModeException();
  151. SelectionKey k = findKey(sel);
  152. if (k != null) {
  153. k.interestOps(ops);
  154. k.attach(att);
  155. }
  156. if (k == null) {
  157. // New registration
  158. k = ((AbstractSelector)sel).register(this, ops, att);
  159. addKey(k);
  160. }
  161. return k;
  162. }
  163. }
  164. // -- Closing --
  165. /**
  166. * Closes this channel.
  167. *
  168. * <p> This method, which is specified in the {@link
  169. * AbstractInterruptibleChannel} class and is invoked by the {@link
  170. * java.nio.channels.Channel#close close} method, in turn invokes the
  171. * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in
  172. * order to perform the actual work of closing this channel. It then
  173. * cancels all of this channel's keys. </p>
  174. */
  175. protected final void implCloseChannel() throws IOException {
  176. implCloseSelectableChannel();
  177. synchronized (keyLock) {
  178. int count = (keys == null) ? 0 : keys.length;
  179. for (int i = 0; i < count; i++) {
  180. SelectionKey k = keys[i];
  181. if (k != null)
  182. k.cancel();
  183. }
  184. }
  185. }
  186. /**
  187. * Closes this selectable channel.
  188. *
  189. * <p> This method is invoked by the {@link java.nio.channels.Channel#close
  190. * close} method in order to perform the actual work of closing the
  191. * channel. This method is only invoked if the channel has not yet been
  192. * closed, and it is never invoked more than once.
  193. *
  194. * <p> An implementation of this method must arrange for any other thread
  195. * that is blocked in an I/O operation upon this channel to return
  196. * immediately, either by throwing an exception or by returning normally.
  197. * </p>
  198. */
  199. protected abstract void implCloseSelectableChannel() throws IOException;
  200. // -- Blocking --
  201. public final boolean isBlocking() {
  202. synchronized (regLock) {
  203. return blocking;
  204. }
  205. }
  206. public final Object blockingLock() {
  207. return regLock;
  208. }
  209. /**
  210. * Adjusts this channel's blocking mode.
  211. *
  212. * <p> If the given blocking mode is different from the current blocking
  213. * mode then this method invokes the {@link #implConfigureBlocking
  214. * implConfigureBlocking} method, while holding the appropriate locks, in
  215. * order to change the mode. </p>
  216. */
  217. public final SelectableChannel configureBlocking(boolean block)
  218. throws IOException
  219. {
  220. synchronized (regLock) {
  221. if (blocking == block)
  222. return this;
  223. if (block && haveValidKeys())
  224. throw new IllegalBlockingModeException();
  225. implConfigureBlocking(block);
  226. blocking = block;
  227. }
  228. return this;
  229. }
  230. /**
  231. * Adjusts this channel's blocking mode.
  232. *
  233. * <p> This method is invoked by the {@link #configureBlocking
  234. * configureBlocking} method in order to perform the actual work of
  235. * changing the blocking mode. This method is only invoked if the new mode
  236. * is different from the current mode. </p>
  237. *
  238. * @throws IOException
  239. * If an I/O error occurs
  240. */
  241. protected abstract void implConfigureBlocking(boolean block)
  242. throws IOException;
  243. }