1 /*
2 * Copyright 2000-2003 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 protected AbstractSelectableChannel(SelectorProvider provider) {
77 this.provider = provider;
78 }
79
80 /**
81 * Returns the provider that created this channel.
82 *
83 * @return The provider that created this channel
84 */
85 public final SelectorProvider provider() {
86 return provider;
87 }
88
89
90 // -- Utility methods for the key set --
91
92 private void addKey(SelectionKey k) {
93 synchronized (keyLock) {
94 int i = 0;
95 if ((keys != null) && (keyCount < keys.length)) {
96 // Find empty element of key array
97 for (i = 0; i < keys.length; i++)
98 if (keys[i] == null)
99 break;
100 } else if (keys == null) {
101 keys = new SelectionKey[3];
102 } else {
103 // Grow key array
104 int n = keys.length * 2;
105 SelectionKey[] ks = new SelectionKey[n];
106 for (i = 0; i < keys.length; i++)
107 ks[i] = keys[i];
108 keys = ks;
109 i = keyCount;
110 }
111 keys[i] = k;
112 keyCount++;
113 }
114 }
115
116 private SelectionKey findKey(Selector sel) {
117 synchronized (keyLock) {
118 if (keys == null)
119 return null;
120 for (int i = 0; i < keys.length; i++)
121 if ((keys[i] != null) && (keys[i].selector() == sel))
122 return keys[i];
123 return null;
124 }
125 }
126
127 void removeKey(SelectionKey k) { // package-private
128 synchronized (keyLock) {
129 for (int i = 0; i < keys.length; i++)
130 if (keys[i] == k) {
131 keys[i] = null;
132 keyCount--;
133 }
134 ((AbstractSelectionKey)k).invalidate();
135 }
136 }
137
138 private boolean haveValidKeys() {
139 synchronized (keyLock) {
140 if (keyCount == 0)
141 return false;
142 for (int i = 0; i < keys.length; i++) {
143 if ((keys[i] != null) && keys[i].isValid())
144 return true;
145 }
146 return false;
147 }
148 }
149
150
151 // -- Registration --
152
153 public final boolean isRegistered() {
154 synchronized (keyLock) {
155 return keyCount != 0;
156 }
157 }
158
159 public final SelectionKey keyFor(Selector sel) {
160 return findKey(sel);
161 }
162
163 /**
164 * Registers this channel with the given selector, returning a selection key.
165 *
166 * <p> This method first verifies that this channel is open and that the
167 * given initial interest set is valid.
168 *
169 * <p> If this channel is already registered with the given selector then
170 * the selection key representing that registration is returned after
171 * setting its interest set to the given value.
172 *
173 * <p> Otherwise this channel has not yet been registered with the given
174 * selector, so the {@link AbstractSelector#register register} method of
175 * the selector is invoked while holding the appropriate locks. The
176 * resulting key is added to this channel's key set before being returned.
177 * </p>
178 */
179 public final SelectionKey register(Selector sel, int ops,
180 Object att)
181 throws ClosedChannelException
182 {
183 if (!isOpen())
184 throw new ClosedChannelException();
185 if ((ops & ~validOps()) != 0)
186 throw new IllegalArgumentException();
187 synchronized (regLock) {
188 if (blocking)
189 throw new IllegalBlockingModeException();
190 SelectionKey k = findKey(sel);
191 if (k != null) {
192 k.interestOps(ops);
193 k.attach(att);
194 }
195 if (k == null) {
196 // New registration
197 k = ((AbstractSelector)sel).register(this, ops, att);
198 addKey(k);
199 }
200 return k;
201 }
202 }
203
204
205 // -- Closing --
206
207 /**
208 * Closes this channel.
209 *
210 * <p> This method, which is specified in the {@link
211 * AbstractInterruptibleChannel} class and is invoked by the {@link
212 * java.nio.channels.Channel#close close} method, in turn invokes the
213 * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in
214 * order to perform the actual work of closing this channel. It then
215 * cancels all of this channel's keys. </p>
216 */
217 protected final void implCloseChannel() throws IOException {
218 implCloseSelectableChannel();
219 synchronized (keyLock) {
220 int count = (keys == null) ? 0 : keys.length;
221 for (int i = 0; i < count; i++) {
222 SelectionKey k = keys[i];
223 if (k != null)
224 k.cancel();
225 }
226 }
227 }
228
229 /**
230 * Closes this selectable channel.
231 *
232 * <p> This method is invoked by the {@link java.nio.channels.Channel#close
233 * close} method in order to perform the actual work of closing the
234 * channel. This method is only invoked if the channel has not yet been
235 * closed, and it is never invoked more than once.
236 *
237 * <p> An implementation of this method must arrange for any other thread
238 * that is blocked in an I/O operation upon this channel to return
239 * immediately, either by throwing an exception or by returning normally.
240 * </p>
241 */
242 protected abstract void implCloseSelectableChannel() throws IOException;
243
244
245 // -- Blocking --
246
247 public final boolean isBlocking() {
248 synchronized (regLock) {
249 return blocking;
250 }
251 }
252
253 public final Object blockingLock() {
254 return regLock;
255 }
256
257 /**
258 * Adjusts this channel's blocking mode.
259 *
260 * <p> If the given blocking mode is different from the current blocking
261 * mode then this method invokes the {@link #implConfigureBlocking
262 * implConfigureBlocking} method, while holding the appropriate locks, in
263 * order to change the mode. </p>
264 */
265 public final SelectableChannel configureBlocking(boolean block)
266 throws IOException
267 {
268 if (!isOpen())
269 throw new ClosedChannelException();
270 synchronized (regLock) {
271 if (blocking == block)
272 return this;
273 if (block && haveValidKeys())
274 throw new IllegalBlockingModeException();
275 implConfigureBlocking(block);
276 blocking = block;
277 }
278 return this;
279 }
280
281 /**
282 * Adjusts this channel's blocking mode.
283 *
284 * <p> This method is invoked by the {@link #configureBlocking
285 * configureBlocking} method in order to perform the actual work of
286 * changing the blocking mode. This method is only invoked if the new mode
287 * is different from the current mode. </p>
288 *
289 * @throws IOException
290 * If an I/O error occurs
291 */
292 protected abstract void implConfigureBlocking(boolean block)
293 throws IOException;
294
295 }