1 /* 2 * Copyright (c) 2000, 2018, 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.CancelledKeyException; 30 import java.nio.channels.ClosedChannelException; 31 import java.nio.channels.ClosedSelectorException; 32 import java.nio.channels.IllegalBlockingModeException; 33 import java.nio.channels.IllegalSelectorException; 34 import java.nio.channels.SelectableChannel; 35 import java.nio.channels.SelectionKey; 36 import java.nio.channels.Selector; 37 38 39 /** 40 * Base implementation class for selectable channels. 41 * 42 * <p> This class defines methods that handle the mechanics of channel 43 * registration, deregistration, and closing. It maintains the current 44 * blocking mode of this channel as well as its current set of selection keys. 45 * It performs all of the synchronization required to implement the {@link 46 * java.nio.channels.SelectableChannel} specification. Implementations of the 47 * abstract protected methods defined in this class need not synchronize 48 * against other threads that might be engaged in the same operations. </p> 49 * 50 * 51 * @author Mark Reinhold 52 * @author Mike McCloskey 53 * @author JSR-51 Expert Group 54 * @since 1.4 55 */ 56 57 public abstract class AbstractSelectableChannel 58 extends SelectableChannel 59 { 60 61 // The provider that created this channel 62 private final SelectorProvider provider; 63 64 // Keys that have been created by registering this channel with selectors. 65 // They are saved because if this channel is closed the keys must be 66 // deregistered. Protected by keyLock. 67 // 68 private SelectionKey[] keys = null; 69 private int keyCount = 0; 70 71 // Lock for key set and count 72 private final Object keyLock = new Object(); 73 74 // Lock for registration and configureBlocking operations 75 private final Object regLock = new Object(); 76 77 // True when non-blocking, need regLock to change; 78 private volatile boolean nonBlocking; 79 80 /** 81 * Initializes a new instance of this class. 82 * 83 * @param provider 84 * The provider that created this channel 85 */ AbstractSelectableChannel(SelectorProvider provider)86 protected AbstractSelectableChannel(SelectorProvider provider) { 87 this.provider = provider; 88 } 89 90 /** 91 * Returns the provider that created this channel. 92 * 93 * @return The provider that created this channel 94 */ provider()95 public final SelectorProvider provider() { 96 return provider; 97 } 98 99 100 // -- Utility methods for the key set -- 101 addKey(SelectionKey k)102 private void addKey(SelectionKey k) { 103 assert Thread.holdsLock(keyLock); 104 int i = 0; 105 if ((keys != null) && (keyCount < keys.length)) { 106 // Find empty element of key array 107 for (i = 0; i < keys.length; i++) 108 if (keys[i] == null) 109 break; 110 } else if (keys == null) { 111 keys = new SelectionKey[2]; 112 } else { 113 // Grow key array 114 int n = keys.length * 2; 115 SelectionKey[] ks = new SelectionKey[n]; 116 for (i = 0; i < keys.length; i++) 117 ks[i] = keys[i]; 118 keys = ks; 119 i = keyCount; 120 } 121 keys[i] = k; 122 keyCount++; 123 } 124 findKey(Selector sel)125 private SelectionKey findKey(Selector sel) { 126 assert Thread.holdsLock(keyLock); 127 if (keys == null) 128 return null; 129 for (int i = 0; i < keys.length; i++) 130 if ((keys[i] != null) && (keys[i].selector() == sel)) 131 return keys[i]; 132 return null; 133 134 } 135 removeKey(SelectionKey k)136 void removeKey(SelectionKey k) { // package-private 137 synchronized (keyLock) { 138 for (int i = 0; i < keys.length; i++) 139 if (keys[i] == k) { 140 keys[i] = null; 141 keyCount--; 142 } 143 ((AbstractSelectionKey)k).invalidate(); 144 } 145 } 146 haveValidKeys()147 private boolean haveValidKeys() { 148 synchronized (keyLock) { 149 if (keyCount == 0) 150 return false; 151 for (int i = 0; i < keys.length; i++) { 152 if ((keys[i] != null) && keys[i].isValid()) 153 return true; 154 } 155 return false; 156 } 157 } 158 159 160 // -- Registration -- 161 isRegistered()162 public final boolean isRegistered() { 163 synchronized (keyLock) { 164 return keyCount != 0; 165 } 166 } 167 keyFor(Selector sel)168 public final SelectionKey keyFor(Selector sel) { 169 synchronized (keyLock) { 170 return findKey(sel); 171 } 172 } 173 174 /** 175 * Registers this channel with the given selector, returning a selection key. 176 * 177 * <p> This method first verifies that this channel is open and that the 178 * given initial interest set is valid. 179 * 180 * <p> If this channel is already registered with the given selector then 181 * the selection key representing that registration is returned after 182 * setting its interest set to the given value. 183 * 184 * <p> Otherwise this channel has not yet been registered with the given 185 * selector, so the {@link AbstractSelector#register register} method of 186 * the selector is invoked while holding the appropriate locks. The 187 * resulting key is added to this channel's key set before being returned. 188 * </p> 189 * 190 * @throws ClosedSelectorException {@inheritDoc} 191 * 192 * @throws IllegalBlockingModeException {@inheritDoc} 193 * 194 * @throws IllegalSelectorException {@inheritDoc} 195 * 196 * @throws CancelledKeyException {@inheritDoc} 197 * 198 * @throws IllegalArgumentException {@inheritDoc} 199 */ register(Selector sel, int ops, Object att)200 public final SelectionKey register(Selector sel, int ops, Object att) 201 throws ClosedChannelException 202 { 203 if ((ops & ~validOps()) != 0) 204 throw new IllegalArgumentException(); 205 if (!isOpen()) 206 throw new ClosedChannelException(); 207 synchronized (regLock) { 208 if (isBlocking()) 209 throw new IllegalBlockingModeException(); 210 synchronized (keyLock) { 211 // re-check if channel has been closed 212 if (!isOpen()) 213 throw new ClosedChannelException(); 214 SelectionKey k = findKey(sel); 215 if (k != null) { 216 k.attach(att); 217 k.interestOps(ops); 218 } else { 219 // New registration 220 k = ((AbstractSelector)sel).register(this, ops, att); 221 addKey(k); 222 } 223 return k; 224 } 225 } 226 } 227 228 229 // -- Closing -- 230 231 /** 232 * Closes this channel. 233 * 234 * <p> This method, which is specified in the {@link 235 * AbstractInterruptibleChannel} class and is invoked by the {@link 236 * java.nio.channels.Channel#close close} method, in turn invokes the 237 * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in 238 * order to perform the actual work of closing this channel. It then 239 * cancels all of this channel's keys. </p> 240 */ implCloseChannel()241 protected final void implCloseChannel() throws IOException { 242 implCloseSelectableChannel(); 243 244 // clone keys to avoid calling cancel when holding keyLock 245 SelectionKey[] copyOfKeys = null; 246 synchronized (keyLock) { 247 if (keys != null) { 248 copyOfKeys = keys.clone(); 249 } 250 } 251 252 if (copyOfKeys != null) { 253 for (SelectionKey k : copyOfKeys) { 254 if (k != null) { 255 k.cancel(); // invalidate and adds key to cancelledKey set 256 } 257 } 258 } 259 } 260 261 /** 262 * Closes this selectable channel. 263 * 264 * <p> This method is invoked by the {@link java.nio.channels.Channel#close 265 * close} method in order to perform the actual work of closing the 266 * channel. This method is only invoked if the channel has not yet been 267 * closed, and it is never invoked more than once. 268 * 269 * <p> An implementation of this method must arrange for any other thread 270 * that is blocked in an I/O operation upon this channel to return 271 * immediately, either by throwing an exception or by returning normally. 272 * </p> 273 * 274 * @throws IOException 275 * If an I/O error occurs 276 */ implCloseSelectableChannel()277 protected abstract void implCloseSelectableChannel() throws IOException; 278 279 280 // -- Blocking -- 281 isBlocking()282 public final boolean isBlocking() { 283 return !nonBlocking; 284 } 285 blockingLock()286 public final Object blockingLock() { 287 return regLock; 288 } 289 290 /** 291 * Adjusts this channel's blocking mode. 292 * 293 * <p> If the given blocking mode is different from the current blocking 294 * mode then this method invokes the {@link #implConfigureBlocking 295 * implConfigureBlocking} method, while holding the appropriate locks, in 296 * order to change the mode. </p> 297 */ configureBlocking(boolean block)298 public final SelectableChannel configureBlocking(boolean block) 299 throws IOException 300 { 301 synchronized (regLock) { 302 if (!isOpen()) 303 throw new ClosedChannelException(); 304 boolean blocking = !nonBlocking; 305 if (block != blocking) { 306 if (block && haveValidKeys()) 307 throw new IllegalBlockingModeException(); 308 implConfigureBlocking(block); 309 nonBlocking = !block; 310 } 311 } 312 return this; 313 } 314 315 /** 316 * Adjusts this channel's blocking mode. 317 * 318 * <p> This method is invoked by the {@link #configureBlocking 319 * configureBlocking} method in order to perform the actual work of 320 * changing the blocking mode. This method is only invoked if the new mode 321 * is different from the current mode. </p> 322 * 323 * @param block If {@code true} then this channel will be placed in 324 * blocking mode; if {@code false} then it will be placed 325 * non-blocking mode 326 * 327 * @throws IOException 328 * If an I/O error occurs 329 */ implConfigureBlocking(boolean block)330 protected abstract void implConfigureBlocking(boolean block) 331 throws IOException; 332 333 } 334