Skip to content

Commit 34cdd7b

Browse files
committed
Hang in PreClose method during dup2 system call
During fcntl, dup2 system calls if read/write happens then PreClose method gets hang. This issue is not seen if we Signal/Kill the thread first and then call the preClose method. Signed-off-by: Shruthi.Shruthi1 <Shruthi.Shruthi1@ibm.com>
1 parent a8becd5 commit 34cdd7b

File tree

1 file changed

+301
-0
lines changed

1 file changed

+301
-0
lines changed
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
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+
/*
27+
* ===========================================================================
28+
* (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved
29+
* ===========================================================================
30+
*/
31+
32+
package sun.nio.ch;
33+
34+
import java.io.FileDescriptor;
35+
import java.io.IOException;
36+
import java.nio.ByteBuffer;
37+
import java.nio.channels.AsynchronousCloseException;
38+
import java.nio.channels.ClosedChannelException;
39+
import java.nio.channels.NotYetConnectedException;
40+
import java.nio.channels.Pipe;
41+
import java.nio.channels.SelectionKey;
42+
import java.nio.channels.spi.SelectorProvider;
43+
import java.util.Objects;
44+
import java.util.concurrent.locks.ReentrantLock;
45+
46+
class SourceChannelImpl
47+
extends Pipe.SourceChannel
48+
implements SelChImpl
49+
{
50+
// Used to make native read and write calls
51+
private static final NativeDispatcher nd = new FileDispatcherImpl();
52+
53+
// The file descriptor associated with this channel
54+
private final FileDescriptor fd;
55+
private final int fdVal;
56+
57+
// Lock held by current reading thread
58+
private final ReentrantLock readLock = new ReentrantLock();
59+
60+
// Lock held by any thread that modifies the state fields declared below
61+
// DO NOT invoke a blocking I/O operation while holding this lock!
62+
private final Object stateLock = new Object();
63+
64+
// -- The following fields are protected by stateLock
65+
66+
// Channel state
67+
private static final int ST_INUSE = 0;
68+
private static final int ST_CLOSING = 1;
69+
private static final int ST_KILLPENDING = 2;
70+
private static final int ST_KILLED = 3;
71+
private int state;
72+
73+
// ID of native thread doing read, for signalling
74+
private long thread;
75+
76+
// -- End of fields protected by stateLock
77+
78+
79+
public FileDescriptor getFD() {
80+
return fd;
81+
}
82+
83+
public int getFDVal() {
84+
return fdVal;
85+
}
86+
87+
SourceChannelImpl(SelectorProvider sp, FileDescriptor fd) {
88+
super(sp);
89+
this.fd = fd;
90+
this.fdVal = IOUtil.fdVal(fd);
91+
}
92+
93+
/**
94+
* Invoked by implCloseChannel to close the channel.
95+
*/
96+
@Override
97+
protected void implCloseSelectableChannel() throws IOException {
98+
assert !isOpen();
99+
100+
boolean interrupted = false;
101+
boolean blocking;
102+
103+
// set state to ST_CLOSING
104+
synchronized (stateLock) {
105+
assert state < ST_CLOSING;
106+
state = ST_CLOSING;
107+
blocking = isBlocking();
108+
}
109+
110+
// wait for any outstanding read to complete
111+
if (blocking) {
112+
synchronized (stateLock) {
113+
assert state == ST_CLOSING;
114+
long th = thread;
115+
if (th != 0) {
116+
NativeThread.signal(th);
117+
nd.preClose(fd);
118+
119+
// wait for read operation to end
120+
while (thread != 0) {
121+
try {
122+
stateLock.wait();
123+
} catch (InterruptedException e) {
124+
interrupted = true;
125+
}
126+
}
127+
}
128+
}
129+
} else {
130+
// non-blocking mode: wait for read to complete
131+
readLock.lock();
132+
readLock.unlock();
133+
}
134+
135+
// set state to ST_KILLPENDING
136+
synchronized (stateLock) {
137+
assert state == ST_CLOSING;
138+
state = ST_KILLPENDING;
139+
}
140+
141+
// close socket if not registered with Selector
142+
if (!isRegistered())
143+
kill();
144+
145+
// restore interrupt status
146+
if (interrupted)
147+
Thread.currentThread().interrupt();
148+
}
149+
150+
@Override
151+
public void kill() throws IOException {
152+
synchronized (stateLock) {
153+
assert thread == 0;
154+
if (state == ST_KILLPENDING) {
155+
state = ST_KILLED;
156+
nd.close(fd);
157+
}
158+
}
159+
}
160+
161+
@Override
162+
protected void implConfigureBlocking(boolean block) throws IOException {
163+
readLock.lock();
164+
try {
165+
synchronized (stateLock) {
166+
IOUtil.configureBlocking(fd, block);
167+
}
168+
} finally {
169+
readLock.unlock();
170+
}
171+
}
172+
173+
public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) {
174+
int intOps = ski.nioInterestOps();
175+
int oldOps = ski.nioReadyOps();
176+
int newOps = initialOps;
177+
178+
if ((ops & Net.POLLNVAL) != 0)
179+
throw new Error("POLLNVAL detected");
180+
181+
if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
182+
newOps = intOps;
183+
ski.nioReadyOps(newOps);
184+
return (newOps & ~oldOps) != 0;
185+
}
186+
187+
if (((ops & Net.POLLIN) != 0) &&
188+
((intOps & SelectionKey.OP_READ) != 0))
189+
newOps |= SelectionKey.OP_READ;
190+
191+
ski.nioReadyOps(newOps);
192+
return (newOps & ~oldOps) != 0;
193+
}
194+
195+
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {
196+
return translateReadyOps(ops, ski.nioReadyOps(), ski);
197+
}
198+
199+
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {
200+
return translateReadyOps(ops, 0, ski);
201+
}
202+
203+
public int translateInterestOps(int ops) {
204+
int newOps = 0;
205+
if (ops == SelectionKey.OP_READ)
206+
newOps |= Net.POLLIN;
207+
return newOps;
208+
}
209+
210+
/**
211+
* Marks the beginning of a read operation that might block.
212+
*
213+
* @throws ClosedChannelException if the channel is closed
214+
* @throws NotYetConnectedException if the channel is not yet connected
215+
*/
216+
private void beginRead(boolean blocking) throws ClosedChannelException {
217+
if (blocking) {
218+
// set hook for Thread.interrupt
219+
begin();
220+
}
221+
synchronized (stateLock) {
222+
if (!isOpen())
223+
throw new ClosedChannelException();
224+
if (blocking)
225+
thread = NativeThread.current();
226+
}
227+
}
228+
229+
/**
230+
* Marks the end of a read operation that may have blocked.
231+
*
232+
* @throws AsynchronousCloseException if the channel was closed due to this
233+
* thread being interrupted on a blocking read operation.
234+
*/
235+
private void endRead(boolean blocking, boolean completed)
236+
throws AsynchronousCloseException
237+
{
238+
if (blocking) {
239+
synchronized (stateLock) {
240+
thread = 0;
241+
// notify any thread waiting in implCloseSelectableChannel
242+
if (state == ST_CLOSING) {
243+
stateLock.notifyAll();
244+
}
245+
}
246+
// remove hook for Thread.interrupt
247+
end(completed);
248+
}
249+
}
250+
251+
@Override
252+
public int read(ByteBuffer dst) throws IOException {
253+
Objects.requireNonNull(dst);
254+
255+
readLock.lock();
256+
try {
257+
boolean blocking = isBlocking();
258+
int n = 0;
259+
try {
260+
beginRead(blocking);
261+
do {
262+
n = IOUtil.read(fd, dst, -1, nd);
263+
} while ((n == IOStatus.INTERRUPTED) && isOpen());
264+
} finally {
265+
endRead(blocking, n > 0);
266+
assert IOStatus.check(n);
267+
}
268+
return IOStatus.normalize(n);
269+
} finally {
270+
readLock.unlock();
271+
}
272+
}
273+
274+
@Override
275+
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
276+
Objects.checkFromIndexSize(offset, length, dsts.length);
277+
278+
readLock.lock();
279+
try {
280+
boolean blocking = isBlocking();
281+
long n = 0;
282+
try {
283+
beginRead(blocking);
284+
do {
285+
n = IOUtil.read(fd, dsts, offset, length, nd);
286+
} while ((n == IOStatus.INTERRUPTED) && isOpen());
287+
} finally {
288+
endRead(blocking, n > 0);
289+
assert IOStatus.check(n);
290+
}
291+
return IOStatus.normalize(n);
292+
} finally {
293+
readLock.unlock();
294+
}
295+
}
296+
297+
@Override
298+
public long read(ByteBuffer[] dsts) throws IOException {
299+
return read(dsts, 0, dsts.length);
300+
}
301+
}

0 commit comments

Comments
 (0)