jdk/src/java.base/windows/native/libnet/SocketOutputStream.c
author alanb
Sun, 19 Oct 2014 11:52:53 +0100
changeset 27178 885f4428b501
parent 25859 3317bb8137f4
child 41771 18c9669e76ca
permissions -rw-r--r--
8060170: Support SIO_LOOPBACK_FAST_PATH option on Windows Reviewed-by: alanb Contributed-by: kirk.shoop@microsoft.com, v-valkop@microsoft.com

/*
 * Copyright (c) 1997, 2008, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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.
 */

#include <windows.h>
#include <winsock2.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>

#include "java_net_SocketOutputStream.h"

#include "net_util.h"
#include "jni_util.h"

/************************************************************************
 * SocketOutputStream
 */
static jfieldID IO_fd_fdID;

/*
 * Class:     java_net_SocketOutputStream
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_init(JNIEnv *env, jclass cls) {
    IO_fd_fdID = NET_GetFileDescriptorID(env);
}

/*
 * Class:     java_net_SocketOutputStream
 * Method:    socketWrite
 * Signature: (Ljava/io/FileDescriptor;[BII)V
 */
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_socketWrite0(JNIEnv *env, jobject this,
                                              jobject fdObj, jbyteArray data,
                                              jint off, jint len) {
    char *bufP;
    char BUF[MAX_BUFFER_LEN];
    int buflen;
    int fd;

    if (IS_NULL(fdObj)) {
        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
        return;
    } else {
        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
    }
    if (IS_NULL(data)) {
        JNU_ThrowNullPointerException(env, "data argument");
        return;
    }

    /*
     * Use stack allocate buffer if possible. For large sizes we allocate
     * an intermediate buffer from the heap (up to a maximum). If heap is
     * unavailable just use our stack buffer.
     */
    if (len <= MAX_BUFFER_LEN) {
        bufP = BUF;
        buflen = MAX_BUFFER_LEN;
    } else {
        buflen = min(MAX_HEAP_BUFFER_LEN, len);
        bufP = (char *)malloc((size_t)buflen);
        if (bufP == NULL) {
            bufP = BUF;
            buflen = MAX_BUFFER_LEN;
        }
    }

    while(len > 0) {
        int loff = 0;
        int chunkLen = min(buflen, len);
        int llen = chunkLen;
        int retry = 0;

        (*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);

        while(llen > 0) {
            int n = send(fd, bufP + loff, llen, 0);
            if (n > 0) {
                llen -= n;
                loff += n;
                continue;
            }

            /*
             * Due to a bug in Windows Sockets (observed on NT and Windows
             * 2000) it may be necessary to retry the send. The issue is that
             * on blocking sockets send/WSASend is supposed to block if there
             * is insufficient buffer space available. If there are a large
             * number of threads blocked on write due to congestion then it's
             * possile to hit the NT/2000 bug whereby send returns WSAENOBUFS.
             * The workaround we use is to retry the send. If we have a
             * large buffer to send (>2k) then we retry with a maximum of
             * 2k buffer. If we hit the issue with <=2k buffer then we backoff
             * for 1 second and retry again. We repeat this up to a reasonable
             * limit before bailing out and throwing an exception. In load
             * conditions we've observed that the send will succeed after 2-3
             * attempts but this depends on network buffers associated with
             * other sockets draining.
             */
            if (WSAGetLastError() == WSAENOBUFS) {
                if (llen > MAX_BUFFER_LEN) {
                    buflen = MAX_BUFFER_LEN;
                    chunkLen = MAX_BUFFER_LEN;
                    llen = MAX_BUFFER_LEN;
                    continue;
                }
                if (retry >= 30) {
                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
                        "No buffer space available - exhausted attempts to queue buffer");
                    if (bufP != BUF) {
                        free(bufP);
                    }
                    return;
                }
                Sleep(1000);
                retry++;
                continue;
            }

            /*
             * Send failed - can be caused by close or write error.
             */
            if (WSAGetLastError() == WSAENOTSOCK) {
                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
            } else {
                NET_ThrowCurrent(env, "socket write error");
            }
            if (bufP != BUF) {
                free(bufP);
            }
            return;
        }
        len -= chunkLen;
        off += chunkLen;
    }

    if (bufP != BUF) {
        free(bufP);
    }
}