1. /*
  2. * @(#)AbstractSelectableChannel.java 1.25 03/12/19
  3. *
  4. * Copyright 2004 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.25, 03/12/19
  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. for (int i = 0; i < keys.length; i++)
  96. if (keys[i] == k) {
  97. keys[i] = null;
  98. keyCount--;
  99. }
  100. ((AbstractSelectionKey)k).invalidate();
  101. }
  102. }
  103. private boolean haveValidKeys() {
  104. synchronized (keyLock) {
  105. if (keyCount == 0)
  106. return false;
  107. for (int i = 0; i < keys.length; i++) {
  108. if ((keys[i] != null) && keys[i].isValid())
  109. return true;
  110. }
  111. return false;
  112. }
  113. }
  114. // -- Registration --
  115. public final boolean isRegistered() {
  116. synchronized (keyLock) {
  117. return keyCount != 0;
  118. }
  119. }
  120. public final SelectionKey keyFor(Selector sel) {
  121. return findKey(sel);
  122. }
  123. /**
  124. * Registers this channel with the given selector, returning a selection key.
  125. *
  126. * <p> This method first verifies that this channel is open and that the
  127. * given initial interest set is valid.
  128. *
  129. * <p> If this channel is already registered with the given selector then
  130. * the selection key representing that registration is returned after
  131. * setting its interest set to the given value.
  132. *
  133. * <p> Otherwise this channel has not yet been registered with the given
  134. * selector, so the {@link AbstractSelector#register register} method of
  135. * the selector is invoked while holding the appropriate locks. The
  136. * resulting key is added to this channel's key set before being returned.
  137. * </p>
  138. */
  139. public final SelectionKey register(Selector sel, int ops,
  140. Object att)
  141. throws ClosedChannelException
  142. {
  143. if (!isOpen())
  144. throw new ClosedChannelException();
  145. if ((ops & ~validOps()) != 0)
  146. throw new IllegalArgumentException();
  147. synchronized (regLock) {
  148. if (blocking)
  149. throw new IllegalBlockingModeException();
  150. SelectionKey k = findKey(sel);
  151. if (k != null) {
  152. k.interestOps(ops);
  153. k.attach(att);
  154. }
  155. if (k == null) {
  156. // New registration
  157. k = ((AbstractSelector)sel).register(this, ops, att);
  158. addKey(k);
  159. }
  160. return k;
  161. }
  162. }
  163. // -- Closing --
  164. /**
  165. * Closes this channel.
  166. *
  167. * <p> This method, which is specified in the {@link
  168. * AbstractInterruptibleChannel} class and is invoked by the {@link
  169. * java.nio.channels.Channel#close close} method, in turn invokes the
  170. * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in
  171. * order to perform the actual work of closing this channel. It then
  172. * cancels all of this channel's keys. </p>
  173. */
  174. protected final void implCloseChannel() throws IOException {
  175. implCloseSelectableChannel();
  176. synchronized (keyLock) {
  177. int count = (keys == null) ? 0 : keys.length;
  178. for (int i = 0; i < count; i++) {
  179. SelectionKey k = keys[i];
  180. if (k != null)
  181. k.cancel();
  182. }
  183. }
  184. }
  185. /**
  186. * Closes this selectable channel.
  187. *
  188. * <p> This method is invoked by the {@link java.nio.channels.Channel#close
  189. * close} method in order to perform the actual work of closing the
  190. * channel. This method is only invoked if the channel has not yet been
  191. * closed, and it is never invoked more than once.
  192. *
  193. * <p> An implementation of this method must arrange for any other thread
  194. * that is blocked in an I/O operation upon this channel to return
  195. * immediately, either by throwing an exception or by returning normally.
  196. * </p>
  197. */
  198. protected abstract void implCloseSelectableChannel() throws IOException;
  199. // -- Blocking --
  200. public final boolean isBlocking() {
  201. synchronized (regLock) {
  202. return blocking;
  203. }
  204. }
  205. public final Object blockingLock() {
  206. return regLock;
  207. }
  208. /**
  209. * Adjusts this channel's blocking mode.
  210. *
  211. * <p> If the given blocking mode is different from the current blocking
  212. * mode then this method invokes the {@link #implConfigureBlocking
  213. * implConfigureBlocking} method, while holding the appropriate locks, in
  214. * order to change the mode. </p>
  215. */
  216. public final SelectableChannel configureBlocking(boolean block)
  217. throws IOException
  218. {
  219. if (!isOpen())
  220. throw new ClosedChannelException();
  221. synchronized (regLock) {
  222. if (blocking == block)
  223. return this;
  224. if (block && haveValidKeys())
  225. throw new IllegalBlockingModeException();
  226. implConfigureBlocking(block);
  227. blocking = block;
  228. }
  229. return this;
  230. }
  231. /**
  232. * Adjusts this channel's blocking mode.
  233. *
  234. * <p> This method is invoked by the {@link #configureBlocking
  235. * configureBlocking} method in order to perform the actual work of
  236. * changing the blocking mode. This method is only invoked if the new mode
  237. * is different from the current mode. </p>
  238. *
  239. * @throws IOException
  240. * If an I/O error occurs
  241. */
  242. protected abstract void implConfigureBlocking(boolean block)
  243. throws IOException;
  244. }