# HG changeset patch # User alanb # Date 1339672434 -3600 # Node ID 2e79ad4fea091e9c194c707816a9016e54484b67 # Parent 8e43ddef9617b0f4a7b90198f7a9511edfb3c877 7176630: (sc) SocketChannel.write does not write more than 128k when channel configured blocking [win] Reviewed-by: khazra, chegar diff -r 8e43ddef9617 -r 2e79ad4fea09 jdk/src/windows/native/sun/nio/ch/SocketDispatcher.c --- a/jdk/src/windows/native/sun/nio/ch/SocketDispatcher.c Wed Jun 13 19:23:23 2012 +0800 +++ b/jdk/src/windows/native/sun/nio/ch/SocketDispatcher.c Thu Jun 14 12:13:54 2012 +0100 @@ -141,41 +141,54 @@ JNIEXPORT jint JNICALL Java_sun_nio_ch_SocketDispatcher_write0(JNIEnv *env, jclass clazz, jobject fdo, - jlong address, jint len) + jlong address, jint total) { /* set up */ int i = 0; DWORD written = 0; + jint count = 0; jint fd = fdval(env, fdo); WSABUF buf; - /* limit size */ - if (len > MAX_BUFFER_SIZE) - len = MAX_BUFFER_SIZE; + do { + /* limit size */ + jint len = total - count; + if (len > MAX_BUFFER_SIZE) + len = MAX_BUFFER_SIZE; - /* copy iovec into WSABUF */ - buf.buf = (char *)address; - buf.len = (u_long)len; + /* copy iovec into WSABUF */ + buf.buf = (char *)address; + buf.len = (u_long)len; + + /* write from the buffer */ + i = WSASend((SOCKET)fd, /* Socket */ + &buf, /* pointers to the buffers */ + (DWORD)1, /* number of buffers to process */ + &written, /* receives number of bytes written */ + 0, /* no flags */ + 0, /* no overlapped sockets */ + 0); /* no completion routine */ - /* read into the buffers */ - i = WSASend((SOCKET)fd, /* Socket */ - &buf, /* pointers to the buffers */ - (DWORD)1, /* number of buffers to process */ - &written, /* receives number of bytes written */ - 0, /* no flags */ - 0, /* no overlapped sockets */ - 0); /* no completion routine */ + if (i == SOCKET_ERROR) { + if (count > 0) { + /* can't throw exception when some bytes have been written */ + break; + } else { + int theErr = (jint)WSAGetLastError(); + if (theErr == WSAEWOULDBLOCK) { + return IOS_UNAVAILABLE; + } + JNU_ThrowIOExceptionWithLastError(env, "Write failed"); + return IOS_THROWN; + } + } - if (i == SOCKET_ERROR) { - int theErr = (jint)WSAGetLastError(); - if (theErr == WSAEWOULDBLOCK) { - return IOS_UNAVAILABLE; - } - JNU_ThrowIOExceptionWithLastError(env, "Write failed"); - return IOS_THROWN; - } + count += written; + address += written; - return convertReturnVal(env, (jint)written, JNI_FALSE); + } while ((count < total) && (written == MAX_BUFFER_SIZE)); + + return count; } JNIEXPORT jlong JNICALL diff -r 8e43ddef9617 -r 2e79ad4fea09 jdk/test/java/nio/channels/SocketChannel/ShortWrite.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/SocketChannel/ShortWrite.java Thu Jun 14 12:13:54 2012 +0100 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2012, 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 7176630 + * @summary Check for short writes on SocketChannels configured in blocking mode + */ + +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.concurrent.*; +import java.util.Random; +import java.util.zip.CRC32; + +public class ShortWrite { + + static final Random rand = new Random(); + + /** + * Returns a checksum on the remaining bytes in the given buffer. + */ + static long computeChecksum(ByteBuffer bb) { + CRC32 crc32 = new CRC32(); + crc32.update(bb); + return crc32.getValue(); + } + + /** + * A task that reads the expected number of bytes and returns the CRC32 + * of those bytes. + */ + static class Reader implements Callable { + final SocketChannel sc; + final ByteBuffer buf; + + Reader(SocketChannel sc, int expectedSize) { + this.sc = sc; + this.buf = ByteBuffer.allocate(expectedSize); + } + + public Long call() throws Exception { + while (buf.hasRemaining()) { + int n = sc.read(buf); + if (n == -1) + throw new RuntimeException("Premature EOF encountered"); + } + buf.flip(); + return computeChecksum(buf); + } + } + + /** + * Run test with a write of the given number of bytes. + */ + static void test(ExecutorService pool, + SocketChannel source, + SocketChannel sink, + int size) + throws Exception + { + System.out.println(size); + + // random bytes in the buffer + ByteBuffer buf = ByteBuffer.allocate(size); + rand.nextBytes(buf.array()); + + // submit task to read the bytes + Future result = pool.submit(new Reader(sink, size)); + + // write the bytes + int n = source.write(buf); + if (n != size) + throw new RuntimeException("Short write detected"); + + // check the bytes that were received match + buf.rewind(); + long expected = computeChecksum(buf); + long actual = result.get(); + if (actual != expected) + throw new RuntimeException("Checksum did not match"); + } + + + public static void main(String[] args) throws Exception { + ExecutorService pool = Executors.newSingleThreadExecutor(); + try { + try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + ssc.bind(new InetSocketAddress(0)); + InetAddress lh = InetAddress.getLocalHost(); + int port = ssc.socket().getLocalPort(); + SocketAddress sa = new InetSocketAddress(lh, port); + + try (SocketChannel source = SocketChannel.open(sa); + SocketChannel sink = ssc.accept()) + { + // run tests on sizes around 128k as that is the problem + // area on Windows. + int BOUNDARY = 128 * 1024; + for (int size=(BOUNDARY-2); size<=(BOUNDARY+2); size++) { + test(pool, source, sink, size); + } + + // run tests on random sizes + for (int i=0; i<20; i++) { + int size = rand.nextInt(1024*1024); + test(pool, source, sink, size); + } + } + } + + } finally { + pool.shutdown(); + } + } +}