changeset 5430:658fdd55e71c

7176630: (sc) SocketChannel.write does not write more than 128k when channel configured blocking [win] Reviewed-by: khazra, chegar
author alanb
date Thu, 14 Jun 2012 12:13:54 +0100
parents f71b6117fd7b
children 1c41d5e49584
files src/windows/native/sun/nio/ch/SocketDispatcher.c test/java/nio/channels/SocketChannel/ShortWrite.java
diffstat 2 files changed, 173 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/windows/native/sun/nio/ch/SocketDispatcher.c	Thu Jun 21 17:41:58 2012 +0400
+++ b/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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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.array());
+        return crc32.getValue();
+    }
+
+    /**
+     * A task that reads the expected number of bytes and returns the CRC32
+     * of those bytes.
+     */
+    static class Reader implements Callable<Long> {
+        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<Long> 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();
+        }
+    }
+}