1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2013, 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 final NativeDispatcher nd = new FileDispatcherImpl();
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 & Net.POLLNVAL) != 0)
123            throw new Error("POLLNVAL detected");
124
125        if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
126            newOps = intOps;
127            sk.nioReadyOps(newOps);
128            return (newOps & ~oldOps) != 0;
129        }
130
131        if (((ops & Net.POLLIN) != 0) &&
132            ((intOps & SelectionKey.OP_READ) != 0))
133            newOps |= SelectionKey.OP_READ;
134
135        sk.nioReadyOps(newOps);
136        return (newOps & ~oldOps) != 0;
137    }
138
139    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
140        return translateReadyOps(ops, sk.nioReadyOps(), sk);
141    }
142
143    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
144        return translateReadyOps(ops, 0, sk);
145    }
146
147    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
148        if (ops == SelectionKey.OP_READ)
149            ops = Net.POLLIN;
150        sk.selector.putEventOps(sk, ops);
151    }
152
153    private void ensureOpen() throws IOException {
154        if (!isOpen())
155            throw new ClosedChannelException();
156    }
157
158    public int read(ByteBuffer dst) throws IOException {
159        if (dst == null) {
160            throw new NullPointerException();
161        }
162        ensureOpen();
163        synchronized (lock) {
164            int n = 0;
165            try {
166                begin();
167                if (!isOpen())
168                    return 0;
169                thread = NativeThread.current();
170                do {
171                    n = IOUtil.read(fd, dst, -1, nd);
172                } while ((n == IOStatus.INTERRUPTED) && isOpen());
173                return IOStatus.normalize(n);
174            } finally {
175                thread = 0;
176                end((n > 0) || (n == IOStatus.UNAVAILABLE));
177                assert IOStatus.check(n);
178            }
179        }
180    }
181
182    public long read(ByteBuffer[] dsts, int offset, int length)
183        throws IOException
184    {
185        if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
186           throw new IndexOutOfBoundsException();
187        return read(Util.subsequence(dsts, offset, length));
188    }
189
190    public long read(ByteBuffer[] dsts) throws IOException {
191        if (dsts == null)
192            throw new NullPointerException();
193        ensureOpen();
194        synchronized (lock) {
195            long n = 0;
196            try {
197                begin();
198                if (!isOpen())
199                    return 0;
200                thread = NativeThread.current();
201                do {
202                    n = IOUtil.read(fd, dsts, nd);
203                } while ((n == IOStatus.INTERRUPTED) && isOpen());
204                return IOStatus.normalize(n);
205            } finally {
206                thread = 0;
207                end((n > 0) || (n == IOStatus.UNAVAILABLE));
208                assert IOStatus.check(n);
209            }
210        }
211    }
212}
213