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 // Blocking mode, 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 */ 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 */ 95 public final SelectorProvider provider() { 96 return provider; 97 } 98 99 100 // -- Utility methods for the key set -- 101 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[3]; 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 125 private SelectionKey findKey(Selector sel) { 126 synchronized (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 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 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 162 public final boolean isRegistered() { 163 synchronized (keyLock) { 164 return keyCount != 0; 165 } 166 } 167 168 public final SelectionKey keyFor(Selector sel) { 169 return findKey(sel); 170 } 171 172 /** 173 * Registers this channel with the given selector, returning a selection key. 174 * 175 * <p> This method first verifies that this channel is open and that the 176 * given initial interest set is valid. 177 * 178 * <p> If this channel is already registered with the given selector then 179 * the selection key representing that registration is returned after 180 * setting its interest set to the given value. 181 * 182 * <p> Otherwise this channel has not yet been registered with the given 183 * selector, so the {@link AbstractSelector#register register} method of 184 * the selector is invoked while holding the appropriate locks. The 185 * resulting key is added to this channel's key set before being returned. 186 * </p> 187 * 188 * @throws ClosedSelectorException {@inheritDoc} 189 * 190 * @throws IllegalBlockingModeException {@inheritDoc} 191 * 192 * @throws IllegalSelectorException {@inheritDoc} 193 * 194 * @throws CancelledKeyException {@inheritDoc} 195 * 196 * @throws IllegalArgumentException {@inheritDoc} 197 */ 198 public final SelectionKey register(Selector sel, int ops, 199 Object att) 200 throws ClosedChannelException 201 { 202 synchronized (regLock) { 203 if (!isOpen()) 204 throw new ClosedChannelException(); 205 if ((ops & ~validOps()) != 0) 206 throw new IllegalArgumentException(); 207 if (isBlocking()) 208 throw new IllegalBlockingModeException(); 209 SelectionKey k = findKey(sel); 210 if (k != null) { 211 k.interestOps(ops); 212 k.attach(att); 213 } 214 if (k == null) { 215 // New registration 216 synchronized (keyLock) { 217 if (!isOpen()) 218 throw new ClosedChannelException(); 219 k = ((AbstractSelector)sel).register(this, ops, att); 220 addKey(k); 221 } 222 } 223 return k; 224 } 225 } 226 227 228 // -- Closing -- 229 230 /** 231 * Closes this channel. 232 * 233 * <p> This method, which is specified in the {@link 234 * AbstractInterruptibleChannel} class and is invoked by the {@link 235 * java.nio.channels.Channel#close close} method, in turn invokes the 236 * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in 237 * order to perform the actual work of closing this channel. It then 238 * cancels all of this channel's keys. </p> 239 */ 240 protected final void implCloseChannel() throws IOException { 241 implCloseSelectableChannel(); 242 synchronized (keyLock) { 243 int count = (keys == null) ? 0 : keys.length; 244 for (int i = 0; i < count; i++) { 245 SelectionKey k = keys[i]; 246 if (k != null) 247 k.cancel(); 248 } 249 } 250 } 251 252 /** 253 * Closes this selectable channel. 254 * 255 * <p> This method is invoked by the {@link java.nio.channels.Channel#close 256 * close} method in order to perform the actual work of closing the 257 * channel. This method is only invoked if the channel has not yet been 258 * closed, and it is never invoked more than once. 259 * 260 * <p> An implementation of this method must arrange for any other thread 261 * that is blocked in an I/O operation upon this channel to return 262 * immediately, either by throwing an exception or by returning normally. 263 * </p> 264 * 265 * @throws IOException 266 * If an I/O error occurs 267 */ 268 protected abstract void implCloseSelectableChannel() throws IOException; 269 270 271 // -- Blocking -- 272 273 public final boolean isBlocking() { 274 return !nonBlocking; 275 } 276 277 public final Object blockingLock() { 278 return regLock; 279 } 280 281 /** 282 * Adjusts this channel's blocking mode. 283 * 284 * <p> If the given blocking mode is different from the current blocking 285 * mode then this method invokes the {@link #implConfigureBlocking 286 * implConfigureBlocking} method, while holding the appropriate locks, in 287 * order to change the mode. </p> 288 */ 289 public final SelectableChannel configureBlocking(boolean block) 290 throws IOException 291 { 292 synchronized (regLock) { 293 if (!isOpen()) 294 throw new ClosedChannelException(); 295 boolean blocking = !nonBlocking; 296 if (block != blocking) { 297 if (block && haveValidKeys()) 298 throw new IllegalBlockingModeException(); 299 implConfigureBlocking(block); 300 nonBlocking = !block; 301 } 302 } 303 return this; 304 } 305 306 /** 307 * Adjusts this channel's blocking mode. 308 * 309 * <p> This method is invoked by the {@link #configureBlocking 310 * configureBlocking} method in order to perform the actual work of 311 * changing the blocking mode. This method is only invoked if the new mode 312 * is different from the current mode. </p> 313 * 314 * @param block If {@code true} then this channel will be placed in 315 * blocking mode; if {@code false} then it will be placed 316 * non-blocking mode 317 * 318 * @throws IOException 319 * If an I/O error occurs 320 */ 321 protected abstract void implConfigureBlocking(boolean block) 322 throws IOException; 323 324 }