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 sun.nio.ch;
27
28import java.io.*;
29import java.nio.ByteBuffer;
30import java.nio.channels.*;
31import java.nio.channels.spi.*;
32
33
34class SinkChannelImpl
35    extends Pipe.SinkChannel
36    implements SelChImpl
37{
38
39    // Used to make native read and write calls
40    private static final NativeDispatcher nd = new FileDispatcherImpl();
41
42    // The file descriptor associated with this channel
43    FileDescriptor fd;
44
45    // fd value needed for dev/poll. This value will remain valid
46    // even after the value in the file descriptor object has been set to -1
47    int fdVal;
48
49    // ID of native thread doing write, for signalling
50    private volatile long thread = 0;
51
52    // Lock held by current reading thread
53    private final Object lock = new Object();
54
55    // Lock held by any thread that modifies the state fields declared below
56    // DO NOT invoke a blocking I/O operation while holding this lock!
57    private final Object stateLock = new Object();
58
59    // -- The following fields are protected by stateLock
60
61    // Channel state
62    private static final int ST_UNINITIALIZED = -1;
63    private static final int ST_INUSE = 0;
64    private static final int ST_KILLED = 1;
65    private volatile int state = ST_UNINITIALIZED;
66
67    // -- End of fields protected by stateLock
68
69
70    public FileDescriptor getFD() {
71        return fd;
72    }
73
74    public int getFDVal() {
75        return fdVal;
76    }
77
78    SinkChannelImpl(SelectorProvider sp, FileDescriptor fd) {
79        super(sp);
80        this.fd = fd;
81        this.fdVal = IOUtil.fdVal(fd);
82        this.state = ST_INUSE;
83    }
84
85    protected void implCloseSelectableChannel() throws IOException {
86        synchronized (stateLock) {
87            if (state != ST_KILLED)
88                nd.preClose(fd);
89            long th = thread;
90            if (th != 0)
91                NativeThread.signal(th);
92            if (!isRegistered())
93                kill();
94        }
95    }
96
97    public void kill() throws IOException {
98        synchronized (stateLock) {
99            if (state == ST_KILLED)
100                return;
101            if (state == ST_UNINITIALIZED) {
102                state = ST_KILLED;
103                return;
104            }
105            assert !isOpen() && !isRegistered();
106            nd.close(fd);
107            state = ST_KILLED;
108        }
109    }
110
111    protected void implConfigureBlocking(boolean block) throws IOException {
112        IOUtil.configureBlocking(fd, block);
113    }
114
115    public boolean translateReadyOps(int ops, int initialOps,
116                                     SelectionKeyImpl sk) {
117        int intOps = sk.nioInterestOps();// Do this just once, it synchronizes
118        int oldOps = sk.nioReadyOps();
119        int newOps = initialOps;
120
121        if ((ops & Net.POLLNVAL) != 0)
122            throw new Error("POLLNVAL detected");
123
124        if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
125            newOps = intOps;
126            sk.nioReadyOps(newOps);
127            return (newOps & ~oldOps) != 0;
128        }
129
130        if (((ops & Net.POLLOUT) != 0) &&
131            ((intOps & SelectionKey.OP_WRITE) != 0))
132            newOps |= SelectionKey.OP_WRITE;
133
134        sk.nioReadyOps(newOps);
135        return (newOps & ~oldOps) != 0;
136    }
137
138    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
139        return translateReadyOps(ops, sk.nioReadyOps(), sk);
140    }
141
142    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
143        return translateReadyOps(ops, 0, sk);
144    }
145
146    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
147        if (ops == SelectionKey.OP_WRITE)
148            ops = Net.POLLOUT;
149        sk.selector.putEventOps(sk, ops);
150    }
151
152    private void ensureOpen() throws IOException {
153        if (!isOpen())
154            throw new ClosedChannelException();
155    }
156
157    public int write(ByteBuffer src) throws IOException {
158        ensureOpen();
159        synchronized (lock) {
160            int n = 0;
161            try {
162                begin();
163                if (!isOpen())
164                    return 0;
165                thread = NativeThread.current();
166                do {
167                    n = IOUtil.write(fd, src, -1, nd);
168                } while ((n == IOStatus.INTERRUPTED) && isOpen());
169                return IOStatus.normalize(n);
170            } finally {
171                thread = 0;
172                end((n > 0) || (n == IOStatus.UNAVAILABLE));
173                assert IOStatus.check(n);
174            }
175        }
176    }
177
178    public long write(ByteBuffer[] srcs) throws IOException {
179        if (srcs == null)
180            throw new NullPointerException();
181        ensureOpen();
182        synchronized (lock) {
183            long n = 0;
184            try {
185                begin();
186                if (!isOpen())
187                    return 0;
188                thread = NativeThread.current();
189                do {
190                    n = IOUtil.write(fd, srcs, nd);
191                } while ((n == IOStatus.INTERRUPTED) && isOpen());
192                return IOStatus.normalize(n);
193            } finally {
194                thread = 0;
195                end((n > 0) || (n == IOStatus.UNAVAILABLE));
196                assert IOStatus.check(n);
197            }
198        }
199    }
200
201    public long write(ByteBuffer[] srcs, int offset, int length)
202        throws IOException
203    {
204        if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
205           throw new IndexOutOfBoundsException();
206        return write(Util.subsequence(srcs, offset, length));
207    }
208}
209