1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package sun.nio.ch;
28
29import java.io.*;
30import java.nio.ByteBuffer;
31import java.nio.channels.*;
32import java.nio.channels.spi.*;
33
34
35class SourceChannelImpl
36    extends Pipe.SourceChannel
37    implements SelChImpl
38{
39
40    // Used to make native read and write calls
41    private static NativeDispatcher nd;
42
43    // The file descriptor associated with this channel
44    FileDescriptor fd;
45
46    // fd value needed for dev/poll. This value will remain valid
47    // even after the value in the file descriptor object has been set to -1
48    int fdVal;
49
50    // ID of native thread doing read, for signalling
51    private volatile long thread = 0;
52
53    // Lock held by current reading thread
54    private final Object lock = new Object();
55
56    // Lock held by any thread that modifies the state fields declared below
57    // DO NOT invoke a blocking I/O operation while holding this lock!
58    private final Object stateLock = new Object();
59
60    // -- The following fields are protected by stateLock
61
62    // Channel state
63    private static final int ST_UNINITIALIZED = -1;
64    private static final int ST_INUSE = 0;
65    private static final int ST_KILLED = 1;
66    private volatile int state = ST_UNINITIALIZED;
67
68    // -- End of fields protected by stateLock
69
70
71    public FileDescriptor getFD() {
72        return fd;
73    }
74
75    public int getFDVal() {
76        return fdVal;
77    }
78
79    SourceChannelImpl(SelectorProvider sp, FileDescriptor fd) {
80        super(sp);
81        this.fd = fd;
82        this.fdVal = IOUtil.fdVal(fd);
83        this.state = ST_INUSE;
84    }
85
86    protected void implCloseSelectableChannel() throws IOException {
87        synchronized (stateLock) {
88            if (state != ST_KILLED)
89                nd.preClose(fd);
90            long th = thread;
91            if (th != 0)
92                NativeThread.signal(th);
93            if (!isRegistered())
94                kill();
95        }
96    }
97
98    public void kill() throws IOException {
99        synchronized (stateLock) {
100            if (state == ST_KILLED)
101                return;
102            if (state == ST_UNINITIALIZED) {
103                state = ST_KILLED;
104                return;
105            }
106            assert !isOpen() && !isRegistered();
107            nd.close(fd);
108            state = ST_KILLED;
109        }
110    }
111
112    protected void implConfigureBlocking(boolean block) throws IOException {
113        IOUtil.configureBlocking(fd, block);
114    }
115
116    public boolean translateReadyOps(int ops, int initialOps,
117                                     SelectionKeyImpl sk) {
118        int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
119        int oldOps = sk.nioReadyOps();
120        int newOps = initialOps;
121
122        if ((ops & PollArrayWrapper.POLLNVAL) != 0)
123            throw new Error("POLLNVAL detected");
124
125        if ((ops & (PollArrayWrapper.POLLERR
126                    | PollArrayWrapper.POLLHUP)) != 0) {
127            newOps = intOps;
128            sk.nioReadyOps(newOps);
129            return (newOps & ~oldOps) != 0;
130        }
131
132        if (((ops & PollArrayWrapper.POLLIN) != 0) &&
133            ((intOps & SelectionKey.OP_READ) != 0))
134            newOps |= SelectionKey.OP_READ;
135
136        sk.nioReadyOps(newOps);
137        return (newOps & ~oldOps) != 0;
138    }
139
140    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
141        return translateReadyOps(ops, sk.nioReadyOps(), sk);
142    }
143
144    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
145        return translateReadyOps(ops, 0, sk);
146    }
147
148    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
149        if (ops == SelectionKey.OP_READ)
150            ops = PollArrayWrapper.POLLIN;
151        sk.selector.putEventOps(sk, ops);
152    }
153
154    private void ensureOpen() throws IOException {
155        if (!isOpen())
156            throw new ClosedChannelException();
157    }
158
159    public int read(ByteBuffer dst) throws IOException {
160        if (dst == null) {
161            throw new NullPointerException();
162        }
163        ensureOpen();
164        synchronized (lock) {
165            int n = 0;
166            try {
167                begin();
168                if (!isOpen())
169                    return 0;
170                thread = NativeThread.current();
171                do {
172                    n = IOUtil.read(fd, dst, -1, nd);
173                } while ((n == IOStatus.INTERRUPTED) && isOpen());
174                return IOStatus.normalize(n);
175            } finally {
176                thread = 0;
177                end((n > 0) || (n == IOStatus.UNAVAILABLE));
178                assert IOStatus.check(n);
179            }
180        }
181    }
182
183    public long read(ByteBuffer[] dsts, int offset, int length)
184        throws IOException
185    {
186        if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
187           throw new IndexOutOfBoundsException();
188        return read(Util.subsequence(dsts, offset, length));
189    }
190
191    public long read(ByteBuffer[] dsts) throws IOException {
192        if (dsts == null)
193            throw new NullPointerException();
194        ensureOpen();
195        synchronized (lock) {
196            long n = 0;
197            try {
198                begin();
199                if (!isOpen())
200                    return 0;
201                thread = NativeThread.current();
202                do {
203                    n = IOUtil.read(fd, dsts, nd);
204                } while ((n == IOStatus.INTERRUPTED) && isOpen());
205                return IOStatus.normalize(n);
206            } finally {
207                thread = 0;
208                end((n > 0) || (n == IOStatus.UNAVAILABLE));
209                assert IOStatus.check(n);
210            }
211        }
212    }
213
214    static {
215        nd = new FileDispatcherImpl();
216    }
217
218}
219