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