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