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
26package java.nio.channels.spi;
27
28import java.io.IOException;
29import 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
50public 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     */
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     */
88    public final SelectorProvider provider() {
89        return provider;
90    }
91
92
93    // -- Utility methods for the key set --
94
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
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
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
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
155    public final boolean isRegistered() {
156        synchronized (keyLock) {
157            return keyCount != 0;
158        }
159    }
160
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     */
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     */
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     */
261    protected abstract void implCloseSelectableChannel() throws IOException;
262
263
264    // -- Blocking --
265
266    public final boolean isBlocking() {
267        synchronized (regLock) {
268            return blocking;
269        }
270    }
271
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     */
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 {@code true} then this channel will be placed in
309     *                blocking mode; if {@code false} then it will be placed
310     *                non-blocking mode
311     *
312     * @throws IOException
313     *         If an I/O error occurs
314     */
315    protected abstract void implConfigureBlocking(boolean block)
316        throws IOException;
317
318}
319