1 /* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.nio.channels.spi; 27 28 import java.io.IOException; 29 import java.nio.channels.*; 30 31 32 /** 33 * Base implementation class for selectable channels. 34 * 35 * <p> This class defines methods that handle the mechanics of channel 36 * registration, deregistration, and closing. It maintains the current 37 * blocking mode of this channel as well as its current set of selection keys. 38 * It performs all of the synchronization required to implement the {@link 39 * java.nio.channels.SelectableChannel} specification. Implementations of the 40 * abstract protected methods defined in this class need not synchronize 41 * against other threads that might be engaged in the same operations. </p> 42 * 43 * 44 * @author Mark Reinhold 45 * @author Mike McCloskey 46 * @author JSR-51 Expert Group 47 * @since 1.4 48 */ 49 50 public abstract class AbstractSelectableChannel 51 extends SelectableChannel 52 { 53 54 // The provider that created this channel 55 private final SelectorProvider provider; 56 57 // Keys that have been created by registering this channel with selectors. 58 // They are saved because if this channel is closed the keys must be 59 // deregistered. Protected by keyLock. 60 // 61 private SelectionKey[] keys = null; 62 private int keyCount = 0; 63 64 // Lock for key set and count 65 private final Object keyLock = new Object(); 66 67 // Lock for registration and configureBlocking operations 68 private final Object regLock = new Object(); 69 70 // Blocking mode, protected by regLock 71 boolean blocking = true; 72 73 /** 74 * Initializes a new instance of this class. 75 * 76 * @param provider 77 * The provider that created this channel 78 */ AbstractSelectableChannel(SelectorProvider provider)79 protected AbstractSelectableChannel(SelectorProvider provider) { 80 this.provider = provider; 81 } 82 83 /** 84 * Returns the provider that created this channel. 85 * 86 * @return The provider that created this channel 87 */ provider()88 public final SelectorProvider provider() { 89 return provider; 90 } 91 92 93 // -- Utility methods for the key set -- 94 addKey(SelectionKey k)95 private void addKey(SelectionKey k) { 96 assert Thread.holdsLock(keyLock); 97 int i = 0; 98 if ((keys != null) && (keyCount < keys.length)) { 99 // Find empty element of key array 100 for (i = 0; i < keys.length; i++) 101 if (keys[i] == null) 102 break; 103 } else if (keys == null) { 104 keys = new SelectionKey[3]; 105 } else { 106 // Grow key array 107 int n = keys.length * 2; 108 SelectionKey[] ks = new SelectionKey[n]; 109 for (i = 0; i < keys.length; i++) 110 ks[i] = keys[i]; 111 keys = ks; 112 i = keyCount; 113 } 114 keys[i] = k; 115 keyCount++; 116 } 117 findKey(Selector sel)118 private SelectionKey findKey(Selector sel) { 119 synchronized (keyLock) { 120 if (keys == null) 121 return null; 122 for (int i = 0; i < keys.length; i++) 123 if ((keys[i] != null) && (keys[i].selector() == sel)) 124 return keys[i]; 125 return null; 126 } 127 } 128 removeKey(SelectionKey k)129 void removeKey(SelectionKey k) { // package-private 130 synchronized (keyLock) { 131 for (int i = 0; i < keys.length; i++) 132 if (keys[i] == k) { 133 keys[i] = null; 134 keyCount--; 135 } 136 ((AbstractSelectionKey)k).invalidate(); 137 } 138 } 139 haveValidKeys()140 private boolean haveValidKeys() { 141 synchronized (keyLock) { 142 if (keyCount == 0) 143 return false; 144 for (int i = 0; i < keys.length; i++) { 145 if ((keys[i] != null) && keys[i].isValid()) 146 return true; 147 } 148 return false; 149 } 150 } 151 152 153 // -- Registration -- 154 isRegistered()155 public final boolean isRegistered() { 156 synchronized (keyLock) { 157 return keyCount != 0; 158 } 159 } 160 keyFor(Selector sel)161 public final SelectionKey keyFor(Selector sel) { 162 return findKey(sel); 163 } 164 165 /** 166 * Registers this channel with the given selector, returning a selection key. 167 * 168 * <p> This method first verifies that this channel is open and that the 169 * given initial interest set is valid. 170 * 171 * <p> If this channel is already registered with the given selector then 172 * the selection key representing that registration is returned after 173 * setting its interest set to the given value. 174 * 175 * <p> Otherwise this channel has not yet been registered with the given 176 * selector, so the {@link AbstractSelector#register register} method of 177 * the selector is invoked while holding the appropriate locks. The 178 * resulting key is added to this channel's key set before being returned. 179 * </p> 180 * 181 * @throws ClosedSelectorException {@inheritDoc} 182 * 183 * @throws IllegalBlockingModeException {@inheritDoc} 184 * 185 * @throws IllegalSelectorException {@inheritDoc} 186 * 187 * @throws CancelledKeyException {@inheritDoc} 188 * 189 * @throws IllegalArgumentException {@inheritDoc} 190 */ register(Selector sel, int ops, Object att)191 public final SelectionKey register(Selector sel, int ops, 192 Object att) 193 throws ClosedChannelException 194 { 195 synchronized (regLock) { 196 if (!isOpen()) 197 throw new ClosedChannelException(); 198 if ((ops & ~validOps()) != 0) 199 throw new IllegalArgumentException(); 200 if (blocking) 201 throw new IllegalBlockingModeException(); 202 SelectionKey k = findKey(sel); 203 if (k != null) { 204 k.interestOps(ops); 205 k.attach(att); 206 } 207 if (k == null) { 208 // New registration 209 synchronized (keyLock) { 210 if (!isOpen()) 211 throw new ClosedChannelException(); 212 k = ((AbstractSelector)sel).register(this, ops, att); 213 addKey(k); 214 } 215 } 216 return k; 217 } 218 } 219 220 221 // -- Closing -- 222 223 /** 224 * Closes this channel. 225 * 226 * <p> This method, which is specified in the {@link 227 * AbstractInterruptibleChannel} class and is invoked by the {@link 228 * java.nio.channels.Channel#close close} method, in turn invokes the 229 * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in 230 * order to perform the actual work of closing this channel. It then 231 * cancels all of this channel's keys. </p> 232 */ implCloseChannel()233 protected final void implCloseChannel() throws IOException { 234 implCloseSelectableChannel(); 235 synchronized (keyLock) { 236 int count = (keys == null) ? 0 : keys.length; 237 for (int i = 0; i < count; i++) { 238 SelectionKey k = keys[i]; 239 if (k != null) 240 k.cancel(); 241 } 242 } 243 } 244 245 /** 246 * Closes this selectable channel. 247 * 248 * <p> This method is invoked by the {@link java.nio.channels.Channel#close 249 * close} method in order to perform the actual work of closing the 250 * channel. This method is only invoked if the channel has not yet been 251 * closed, and it is never invoked more than once. 252 * 253 * <p> An implementation of this method must arrange for any other thread 254 * that is blocked in an I/O operation upon this channel to return 255 * immediately, either by throwing an exception or by returning normally. 256 * </p> 257 * 258 * @throws IOException 259 * If an I/O error occurs 260 */ implCloseSelectableChannel()261 protected abstract void implCloseSelectableChannel() throws IOException; 262 263 264 // -- Blocking -- 265 isBlocking()266 public final boolean isBlocking() { 267 synchronized (regLock) { 268 return blocking; 269 } 270 } 271 blockingLock()272 public final Object blockingLock() { 273 return regLock; 274 } 275 276 /** 277 * Adjusts this channel's blocking mode. 278 * 279 * <p> If the given blocking mode is different from the current blocking 280 * mode then this method invokes the {@link #implConfigureBlocking 281 * implConfigureBlocking} method, while holding the appropriate locks, in 282 * order to change the mode. </p> 283 */ configureBlocking(boolean block)284 public final SelectableChannel configureBlocking(boolean block) 285 throws IOException 286 { 287 synchronized (regLock) { 288 if (!isOpen()) 289 throw new ClosedChannelException(); 290 if (blocking == block) 291 return this; 292 if (block && haveValidKeys()) 293 throw new IllegalBlockingModeException(); 294 implConfigureBlocking(block); 295 blocking = block; 296 } 297 return this; 298 } 299 300 /** 301 * Adjusts this channel's blocking mode. 302 * 303 * <p> This method is invoked by the {@link #configureBlocking 304 * configureBlocking} method in order to perform the actual work of 305 * changing the blocking mode. This method is only invoked if the new mode 306 * is different from the current mode. </p> 307 * 308 * @param block If <tt>true</tt> then this channel will be placed in 309 * blocking mode; if <tt>false</tt> then it will be placed 310 * non-blocking mode 311 * 312 * @throws IOException 313 * If an I/O error occurs 314 */ implConfigureBlocking(boolean block)315 protected abstract void implConfigureBlocking(boolean block) 316 throws IOException; 317 318 } 319