8aae88b0fc
Reviewed-by: dfuchs
523 lines
16 KiB
Java
523 lines
16 KiB
Java
/*
|
|
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/* @test
|
|
* @bug 8222774 4430139
|
|
* @run testng AdaptorStreams
|
|
* @summary Exercise socket adaptor input/output streams
|
|
*/
|
|
|
|
import java.io.Closeable;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.InetAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
import java.net.SocketTimeoutException;
|
|
import java.nio.channels.IllegalBlockingModeException;
|
|
import java.nio.channels.SocketChannel;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.Future;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import org.testng.annotations.Test;
|
|
import static org.testng.Assert.*;
|
|
|
|
@Test
|
|
public class AdaptorStreams {
|
|
|
|
/**
|
|
* Test read when bytes are available
|
|
*/
|
|
public void testRead1() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
peer.getOutputStream().write(99);
|
|
int n = sc.socket().getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test read blocking before bytes are available
|
|
*/
|
|
public void testRead2() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
scheduleWrite(peer.getOutputStream(), 99, 1000);
|
|
int n = sc.socket().getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test read when peer has closed connection
|
|
*/
|
|
public void testRead3() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
peer.close();
|
|
int n = sc.socket().getInputStream().read();
|
|
assertEquals(n, -1);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test read blocking before peer closes connection
|
|
*/
|
|
public void testRead4() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
scheduleClose(peer, 1000);
|
|
int n = sc.socket().getInputStream().read();
|
|
assertEquals(n, -1);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test async close of socket when thread blocked in read
|
|
*/
|
|
public void testRead5() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
scheduleClose(sc, 2000);
|
|
InputStream in = sc.socket().getInputStream();
|
|
expectThrows(IOException.class, () -> in.read());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test interrupt status set before read
|
|
*/
|
|
public void testRead6() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
Thread.currentThread().interrupt();
|
|
try {
|
|
InputStream in = s.getInputStream();
|
|
expectThrows(IOException.class, () -> in.read());
|
|
} finally {
|
|
Thread.interrupted(); // clear interrupt
|
|
}
|
|
assertTrue(s.isClosed());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test interrupt of thread blocked in read
|
|
*/
|
|
public void testRead7() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Future<?> interrupter = scheduleInterrupt(Thread.currentThread(), 2000);
|
|
Socket s = sc.socket();
|
|
try {
|
|
InputStream in = s.getInputStream();
|
|
expectThrows(IOException.class, () -> in.read());
|
|
} finally {
|
|
interrupter.cancel(true);
|
|
Thread.interrupted(); // clear interrupt
|
|
}
|
|
assertTrue(s.isClosed());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test read when channel is configured non-blocking
|
|
*/
|
|
public void testRead8() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
sc.configureBlocking(false);
|
|
InputStream in = sc.socket().getInputStream();
|
|
expectThrows(IllegalBlockingModeException.class, () -> in.read());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test timed read when bytes are available
|
|
*/
|
|
public void testTimedRead1() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
peer.getOutputStream().write(99);
|
|
Socket s = sc.socket();
|
|
s.setSoTimeout(60_000);
|
|
int n = s.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test timed read blocking before bytes are available
|
|
*/
|
|
public void testTimedRead2() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
scheduleWrite(peer.getOutputStream(), 99, 1000);
|
|
Socket s = sc.socket();
|
|
s.setSoTimeout(60_000);
|
|
int n = s.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test timed read when the read times out
|
|
*/
|
|
public void testTimedRead3() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
s.setSoTimeout(500);
|
|
InputStream in = s.getInputStream();
|
|
expectThrows(SocketTimeoutException.class, () -> in.read());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test async close of socket when thread blocked in timed read
|
|
*/
|
|
public void testTimedRead4() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
scheduleClose(sc, 2000);
|
|
Socket s = sc.socket();
|
|
s.setSoTimeout(60_000);
|
|
InputStream in = s.getInputStream();
|
|
expectThrows(IOException.class, () -> in.read());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test interrupt status set before timed read
|
|
*/
|
|
public void testTimedRead5() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
Thread.currentThread().interrupt();
|
|
try {
|
|
s.setSoTimeout(60_000);
|
|
InputStream in = s.getInputStream();
|
|
expectThrows(IOException.class, () -> in.read());
|
|
} finally {
|
|
Thread.interrupted(); // clear interrupt
|
|
}
|
|
assertTrue(s.isClosed());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test interrupt of thread blocked in timed read
|
|
*/
|
|
public void testTimedRead6() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Future<?> interrupter = scheduleInterrupt(Thread.currentThread(), 2000);
|
|
Socket s = sc.socket();
|
|
try {
|
|
s.setSoTimeout(60_000);
|
|
InputStream in = s.getInputStream();
|
|
expectThrows(IOException.class, () -> in.read());
|
|
assertTrue(s.isClosed());
|
|
} finally {
|
|
interrupter.cancel(true);
|
|
Thread.interrupted(); // clear interrupt
|
|
}
|
|
assertTrue(s.isClosed());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test async close of socket when thread blocked in write
|
|
*/
|
|
public void testWrite1() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
scheduleClose(sc, 2000);
|
|
expectThrows(IOException.class, () -> {
|
|
OutputStream out = sc.socket().getOutputStream();
|
|
byte[] data = new byte[64*1000];
|
|
while (true) {
|
|
out.write(data);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test interrupt status set before write
|
|
*/
|
|
public void testWrite2() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
Thread.currentThread().interrupt();
|
|
try {
|
|
OutputStream out = s.getOutputStream();
|
|
expectThrows(IOException.class, () -> out.write(99));
|
|
} finally {
|
|
Thread.interrupted(); // clear interrupt
|
|
}
|
|
assertTrue(s.isClosed());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test interrupt of thread blocked in write
|
|
*/
|
|
public void testWrite3() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Future<?> interrupter = scheduleInterrupt(Thread.currentThread(), 2000);
|
|
Socket s = sc.socket();
|
|
try {
|
|
expectThrows(IOException.class, () -> {
|
|
OutputStream out = sc.socket().getOutputStream();
|
|
byte[] data = new byte[64*1000];
|
|
while (true) {
|
|
out.write(data);
|
|
}
|
|
});
|
|
} finally {
|
|
interrupter.cancel(true);
|
|
Thread.interrupted(); // clear interrupt
|
|
}
|
|
assertTrue(s.isClosed());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test write when channel is configured non-blocking
|
|
*/
|
|
public void testWrite4() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
sc.configureBlocking(false);
|
|
OutputStream out = sc.socket().getOutputStream();
|
|
expectThrows(IllegalBlockingModeException.class, () -> out.write(99));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test read when there are bytes available and another thread is blocked
|
|
* in write
|
|
*/
|
|
public void testConcurrentReadWrite1() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
|
|
// block thread in write
|
|
execute(() -> {
|
|
var data = new byte[64*1024];
|
|
OutputStream out = s.getOutputStream();
|
|
for (;;) {
|
|
out.write(data);
|
|
}
|
|
});
|
|
Thread.sleep(1000); // give writer time to block
|
|
|
|
// test read when bytes are available
|
|
peer.getOutputStream().write(99);
|
|
int n = s.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test read blocking when another thread is blocked in write
|
|
*/
|
|
public void testConcurrentReadWrite2() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
|
|
// block thread in write
|
|
execute(() -> {
|
|
var data = new byte[64*1024];
|
|
OutputStream out = s.getOutputStream();
|
|
for (;;) {
|
|
out.write(data);
|
|
}
|
|
});
|
|
Thread.sleep(1000); // give writer time to block
|
|
|
|
// test read blocking until bytes are available
|
|
scheduleWrite(peer.getOutputStream(), 99, 500);
|
|
int n = s.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test writing when another thread is blocked in read
|
|
*/
|
|
public void testConcurrentReadWrite3() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
|
|
// block thread in read
|
|
execute(() -> {
|
|
s.getInputStream().read();
|
|
});
|
|
Thread.sleep(100); // give reader time to block
|
|
|
|
// test write
|
|
s.getOutputStream().write(99);
|
|
int n = peer.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test timed read when there are bytes available and another thread is
|
|
* blocked in write
|
|
*/
|
|
public void testConcurrentTimedReadWrite1() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
|
|
// block thread in write
|
|
execute(() -> {
|
|
var data = new byte[64*1024];
|
|
OutputStream out = s.getOutputStream();
|
|
for (;;) {
|
|
out.write(data);
|
|
}
|
|
});
|
|
Thread.sleep(1000); // give writer time to block
|
|
|
|
// test read when bytes are available
|
|
peer.getOutputStream().write(99);
|
|
s.setSoTimeout(60_000);
|
|
int n = s.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test timed read blocking when another thread is blocked in write
|
|
*/
|
|
public void testConcurrentTimedReadWrite2() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
|
|
// block thread in write
|
|
execute(() -> {
|
|
var data = new byte[64*1024];
|
|
OutputStream out = s.getOutputStream();
|
|
for (;;) {
|
|
out.write(data);
|
|
}
|
|
});
|
|
Thread.sleep(1000); // give writer time to block
|
|
|
|
// test read blocking until bytes are available
|
|
scheduleWrite(peer.getOutputStream(), 99, 500);
|
|
s.setSoTimeout(60_000);
|
|
int n = s.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test writing when another thread is blocked in read
|
|
*/
|
|
public void testConcurrentTimedReadWrite3() throws Exception {
|
|
withConnection((sc, peer) -> {
|
|
Socket s = sc.socket();
|
|
|
|
// block thread in read
|
|
execute(() -> {
|
|
s.setSoTimeout(60_000);
|
|
s.getInputStream().read();
|
|
});
|
|
Thread.sleep(100); // give reader time to block
|
|
|
|
// test write
|
|
s.getOutputStream().write(99);
|
|
int n = peer.getInputStream().read();
|
|
assertEquals(n, 99);
|
|
});
|
|
}
|
|
|
|
// -- test infrastructure --
|
|
|
|
interface ThrowingTask {
|
|
void run() throws Exception;
|
|
}
|
|
|
|
interface ThrowingBiConsumer<T, U> {
|
|
void accept(T t, U u) throws Exception;
|
|
}
|
|
|
|
/**
|
|
* Invokes the consumer with a connected pair of socket channel and socket
|
|
*/
|
|
static void withConnection(ThrowingBiConsumer<SocketChannel, Socket> consumer)
|
|
throws Exception
|
|
{
|
|
var loopback = InetAddress.getLoopbackAddress();
|
|
try (ServerSocket ss = new ServerSocket()) {
|
|
ss.bind(new InetSocketAddress(loopback, 0));
|
|
try (SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress())) {
|
|
try (Socket peer = ss.accept()) {
|
|
consumer.accept(sc, peer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Future<?> scheduleWrite(OutputStream out, byte[] data, long delay) {
|
|
return schedule(() -> {
|
|
try {
|
|
out.write(data);
|
|
} catch (IOException ioe) { }
|
|
}, delay);
|
|
}
|
|
|
|
static Future<?> scheduleWrite(OutputStream out, int b, long delay) {
|
|
return scheduleWrite(out, new byte[] { (byte)b }, delay);
|
|
}
|
|
|
|
static Future<?> scheduleClose(Closeable c, long delay) {
|
|
return schedule(() -> {
|
|
try {
|
|
c.close();
|
|
} catch (IOException ioe) { }
|
|
}, delay);
|
|
}
|
|
|
|
static Future<?> scheduleInterrupt(Thread t, long delay) {
|
|
return schedule(() -> t.interrupt(), delay);
|
|
}
|
|
|
|
static Future<?> schedule(Runnable task, long delay) {
|
|
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
|
try {
|
|
return executor.schedule(task, delay, TimeUnit.MILLISECONDS);
|
|
} finally {
|
|
executor.shutdown();
|
|
}
|
|
}
|
|
|
|
static Future<?> execute(ThrowingTask task) {
|
|
ExecutorService pool = Executors.newFixedThreadPool(1);
|
|
try {
|
|
return pool.submit(() -> {
|
|
task.run();
|
|
return null;
|
|
});
|
|
} finally {
|
|
pool.shutdown();
|
|
}
|
|
}
|
|
}
|