001 /*
002 * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.nio.channels.spi;
027
028 import java.io.IOException;
029 import java.nio.channels.*;
030
031 /**
032 * Base implementation class for selectable channels.
033 *
034 * <p> This class defines methods that handle the mechanics of channel
035 * registration, deregistration, and closing. It maintains the current
036 * blocking mode of this channel as well as its current set of selection keys.
037 * It performs all of the synchronization required to implement the {@link
038 * java.nio.channels.SelectableChannel} specification. Implementations of the
039 * abstract protected methods defined in this class need not synchronize
040 * against other threads that might be engaged in the same operations. </p>
041 *
042 *
043 * @author Mark Reinhold
044 * @author Mike McCloskey
045 * @author JSR-51 Expert Group
046 * @version 1.32, 07/05/05
047 * @since 1.4
048 */
049
050 public abstract class AbstractSelectableChannel extends
051 SelectableChannel {
052
053 // The provider that created this channel
054 private final SelectorProvider provider;
055
056 // Keys that have been created by registering this channel with selectors.
057 // They are saved because if this channel is closed the keys must be
058 // deregistered. Protected by keyLock.
059 //
060 private SelectionKey[] keys = null;
061 private int keyCount = 0;
062
063 // Lock for key set and count
064 private final Object keyLock = new Object();
065
066 // Lock for registration and configureBlocking operations
067 private final Object regLock = new Object();
068
069 // Blocking mode, protected by regLock
070 boolean blocking = true;
071
072 /**
073 * Initializes a new instance of this class.
074 */
075 protected AbstractSelectableChannel(SelectorProvider provider) {
076 this .provider = provider;
077 }
078
079 /**
080 * Returns the provider that created this channel.
081 *
082 * @return The provider that created this channel
083 */
084 public final SelectorProvider provider() {
085 return provider;
086 }
087
088 // -- Utility methods for the key set --
089
090 private void addKey(SelectionKey k) {
091 synchronized (keyLock) {
092 int i = 0;
093 if ((keys != null) && (keyCount < keys.length)) {
094 // Find empty element of key array
095 for (i = 0; i < keys.length; i++)
096 if (keys[i] == null)
097 break;
098 } else if (keys == null) {
099 keys = new SelectionKey[3];
100 } else {
101 // Grow key array
102 int n = keys.length * 2;
103 SelectionKey[] ks = new SelectionKey[n];
104 for (i = 0; i < keys.length; i++)
105 ks[i] = keys[i];
106 keys = ks;
107 i = keyCount;
108 }
109 keys[i] = k;
110 keyCount++;
111 }
112 }
113
114 private SelectionKey findKey(Selector sel) {
115 synchronized (keyLock) {
116 if (keys == null)
117 return null;
118 for (int i = 0; i < keys.length; i++)
119 if ((keys[i] != null) && (keys[i].selector() == sel))
120 return keys[i];
121 return null;
122 }
123 }
124
125 void removeKey(SelectionKey k) { // package-private
126 synchronized (keyLock) {
127 for (int i = 0; i < keys.length; i++)
128 if (keys[i] == k) {
129 keys[i] = null;
130 keyCount--;
131 }
132 ((AbstractSelectionKey) k).invalidate();
133 }
134 }
135
136 private boolean haveValidKeys() {
137 synchronized (keyLock) {
138 if (keyCount == 0)
139 return false;
140 for (int i = 0; i < keys.length; i++) {
141 if ((keys[i] != null) && keys[i].isValid())
142 return true;
143 }
144 return false;
145 }
146 }
147
148 // -- Registration --
149
150 public final boolean isRegistered() {
151 synchronized (keyLock) {
152 return keyCount != 0;
153 }
154 }
155
156 public final SelectionKey keyFor(Selector sel) {
157 return findKey(sel);
158 }
159
160 /**
161 * Registers this channel with the given selector, returning a selection key.
162 *
163 * <p> This method first verifies that this channel is open and that the
164 * given initial interest set is valid.
165 *
166 * <p> If this channel is already registered with the given selector then
167 * the selection key representing that registration is returned after
168 * setting its interest set to the given value.
169 *
170 * <p> Otherwise this channel has not yet been registered with the given
171 * selector, so the {@link AbstractSelector#register register} method of
172 * the selector is invoked while holding the appropriate locks. The
173 * resulting key is added to this channel's key set before being returned.
174 * </p>
175 */
176 public final SelectionKey register(Selector sel, int ops, Object att)
177 throws ClosedChannelException {
178 if (!isOpen())
179 throw new ClosedChannelException();
180 if ((ops & ~validOps()) != 0)
181 throw new IllegalArgumentException();
182 synchronized (regLock) {
183 if (blocking)
184 throw new IllegalBlockingModeException();
185 SelectionKey k = findKey(sel);
186 if (k != null) {
187 k.interestOps(ops);
188 k.attach(att);
189 }
190 if (k == null) {
191 // New registration
192 k = ((AbstractSelector) sel).register(this , ops, att);
193 addKey(k);
194 }
195 return k;
196 }
197 }
198
199 // -- Closing --
200
201 /**
202 * Closes this channel.
203 *
204 * <p> This method, which is specified in the {@link
205 * AbstractInterruptibleChannel} class and is invoked by the {@link
206 * java.nio.channels.Channel#close close} method, in turn invokes the
207 * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in
208 * order to perform the actual work of closing this channel. It then
209 * cancels all of this channel's keys. </p>
210 */
211 protected final void implCloseChannel() throws IOException {
212 implCloseSelectableChannel();
213 synchronized (keyLock) {
214 int count = (keys == null) ? 0 : keys.length;
215 for (int i = 0; i < count; i++) {
216 SelectionKey k = keys[i];
217 if (k != null)
218 k.cancel();
219 }
220 }
221 }
222
223 /**
224 * Closes this selectable channel.
225 *
226 * <p> This method is invoked by the {@link java.nio.channels.Channel#close
227 * close} method in order to perform the actual work of closing the
228 * channel. This method is only invoked if the channel has not yet been
229 * closed, and it is never invoked more than once.
230 *
231 * <p> An implementation of this method must arrange for any other thread
232 * that is blocked in an I/O operation upon this channel to return
233 * immediately, either by throwing an exception or by returning normally.
234 * </p>
235 */
236 protected abstract void implCloseSelectableChannel()
237 throws IOException;
238
239 // -- Blocking --
240
241 public final boolean isBlocking() {
242 synchronized (regLock) {
243 return blocking;
244 }
245 }
246
247 public final Object blockingLock() {
248 return regLock;
249 }
250
251 /**
252 * Adjusts this channel's blocking mode.
253 *
254 * <p> If the given blocking mode is different from the current blocking
255 * mode then this method invokes the {@link #implConfigureBlocking
256 * implConfigureBlocking} method, while holding the appropriate locks, in
257 * order to change the mode. </p>
258 */
259 public final SelectableChannel configureBlocking(boolean block)
260 throws IOException {
261 if (!isOpen())
262 throw new ClosedChannelException();
263 synchronized (regLock) {
264 if (blocking == block)
265 return this ;
266 if (block && haveValidKeys())
267 throw new IllegalBlockingModeException();
268 implConfigureBlocking(block);
269 blocking = block;
270 }
271 return this ;
272 }
273
274 /**
275 * Adjusts this channel's blocking mode.
276 *
277 * <p> This method is invoked by the {@link #configureBlocking
278 * configureBlocking} method in order to perform the actual work of
279 * changing the blocking mode. This method is only invoked if the new mode
280 * is different from the current mode. </p>
281 *
282 * @throws IOException
283 * If an I/O error occurs
284 */
285 protected abstract void implConfigureBlocking(boolean block)
286 throws IOException;
287
288 }
|