mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-15 08:34:30 +02:00
8295792: Clean up old async close code
Reviewed-by: alanb
This commit is contained in:
parent
5ac6f185ee
commit
2f3f3b6185
12 changed files with 4 additions and 1159 deletions
|
@ -54,7 +54,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBNET, \
|
|||
$(call SET_SHARED_LIBRARY_ORIGIN), \
|
||||
LDFLAGS_windows := -delayload:secur32.dll -delayload:iphlpapi.dll, \
|
||||
LIBS_unix := -ljvm -ljava, \
|
||||
LIBS_linux := $(LIBDL) -lpthread, \
|
||||
LIBS_linux := $(LIBDL), \
|
||||
LIBS_aix := $(LIBDL),\
|
||||
LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib winhttp.lib \
|
||||
delayimp.lib $(WIN_JAVA_LIB) advapi32.lib, \
|
||||
|
|
|
@ -1,378 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2019 SAP SE 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains implementations of NET_... functions. The NET_.. functions are
|
||||
* wrappers for common file- and socket functions plus provisions for non-blocking IO.
|
||||
*
|
||||
* (basically, the layers remember all file descriptors waiting for a particular fd;
|
||||
* all threads waiting on a certain fd can be woken up by sending them a signal; this
|
||||
* is done e.g. when the fd is closed.)
|
||||
*
|
||||
* This was originally copied from the linux_close.c implementation.
|
||||
*
|
||||
* Side Note: This coding needs initialization. Under Linux this is done
|
||||
* automatically via __attribute((constructor)), on AIX this is done manually
|
||||
* (see aix_close_init).
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
AIX needs a workaround for I/O cancellation, see:
|
||||
http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/close.htm
|
||||
...
|
||||
The close subroutine is blocked until all subroutines which use the file
|
||||
descriptor return to usr space. For example, when a thread is calling close
|
||||
and another thread is calling select with the same file descriptor, the
|
||||
close subroutine does not return until the select call returns.
|
||||
...
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include "jvm.h"
|
||||
#include "net_util.h"
|
||||
|
||||
/*
|
||||
* Stack allocated by thread when doing blocking operation
|
||||
*/
|
||||
typedef struct threadEntry {
|
||||
pthread_t thr; /* this thread */
|
||||
struct threadEntry *next; /* next thread */
|
||||
int intr; /* interrupted */
|
||||
} threadEntry_t;
|
||||
|
||||
/*
|
||||
* Heap allocated during initialized - one entry per fd
|
||||
*/
|
||||
typedef struct {
|
||||
pthread_mutex_t lock; /* fd lock */
|
||||
threadEntry_t *threads; /* threads blocked on fd */
|
||||
} fdEntry_t;
|
||||
|
||||
/*
|
||||
* Signal to unblock thread
|
||||
*/
|
||||
static int sigWakeup = (SIGRTMAX - 1);
|
||||
|
||||
/*
|
||||
* fdTable holds one entry per file descriptor, up to a certain
|
||||
* maximum.
|
||||
* Theoretically, the number of possible file descriptors can get
|
||||
* large, though usually it does not. Entries for small value file
|
||||
* descriptors are kept in a simple table, which covers most scenarios.
|
||||
* Entries for large value file descriptors are kept in an overflow
|
||||
* table, which is organized as a sparse two dimensional array whose
|
||||
* slabs are allocated on demand. This covers all corner cases while
|
||||
* keeping memory consumption reasonable.
|
||||
*/
|
||||
|
||||
/* Base table for low value file descriptors */
|
||||
static fdEntry_t* fdTable = NULL;
|
||||
/* Maximum size of base table (in number of entries). */
|
||||
static const int fdTableMaxSize = 0x1000; /* 4K */
|
||||
/* Actual size of base table (in number of entries) */
|
||||
static int fdTableLen = 0;
|
||||
/* Max. theoretical number of file descriptors on system. */
|
||||
static int fdLimit = 0;
|
||||
|
||||
/* Overflow table, should base table not be large enough. Organized as
|
||||
* an array of n slabs, each holding 64k entries.
|
||||
*/
|
||||
static fdEntry_t** fdOverflowTable = NULL;
|
||||
/* Number of slabs in the overflow table */
|
||||
static int fdOverflowTableLen = 0;
|
||||
/* Number of entries in one slab */
|
||||
static const int fdOverflowTableSlabSize = 0x10000; /* 64k */
|
||||
pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Null signal handler
|
||||
*/
|
||||
static void sig_wakeup(int sig) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization routine (executed when library is loaded)
|
||||
* Allocate fd tables and sets up signal handler.
|
||||
*
|
||||
* On AIX we don't have __attribute((constructor)) so we need to initialize
|
||||
* manually (from JNI_OnLoad() in 'src/share/native/java/net/net_util.c')
|
||||
*/
|
||||
void aix_close_init() {
|
||||
struct rlimit nbr_files;
|
||||
sigset_t sigset;
|
||||
struct sigaction sa;
|
||||
int i = 0;
|
||||
|
||||
/* Determine the maximum number of possible file descriptors. */
|
||||
if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to get max # of allocated fds\n");
|
||||
abort();
|
||||
}
|
||||
if (nbr_files.rlim_max != RLIM_INFINITY) {
|
||||
fdLimit = nbr_files.rlim_max;
|
||||
} else {
|
||||
/* We just do not know. */
|
||||
fdLimit = INT_MAX;
|
||||
}
|
||||
|
||||
/* Allocate table for low value file descriptors. */
|
||||
fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;
|
||||
fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));
|
||||
if (fdTable == NULL) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to allocate file descriptor table - out of memory");
|
||||
abort();
|
||||
} else {
|
||||
for (i = 0; i < fdTableLen; i ++) {
|
||||
pthread_mutex_init(&fdTable[i].lock, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate overflow table, if needed */
|
||||
if (fdLimit > fdTableMaxSize) {
|
||||
fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;
|
||||
fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));
|
||||
if (fdOverflowTable == NULL) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to allocate file descriptor overflow table - out of memory");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the signal handler
|
||||
*/
|
||||
sa.sa_handler = sig_wakeup;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(sigWakeup, &sa, NULL);
|
||||
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, sigWakeup);
|
||||
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the fd table for this fd.
|
||||
*/
|
||||
static inline fdEntry_t *getFdEntry(int fd)
|
||||
{
|
||||
fdEntry_t* result = NULL;
|
||||
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This should not happen. If it does, our assumption about
|
||||
* max. fd value was wrong. */
|
||||
assert(fd < fdLimit);
|
||||
|
||||
if (fd < fdTableMaxSize) {
|
||||
/* fd is in base table. */
|
||||
assert(fd < fdTableLen);
|
||||
result = &fdTable[fd];
|
||||
} else {
|
||||
/* fd is in overflow table. */
|
||||
const int indexInOverflowTable = fd - fdTableMaxSize;
|
||||
const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;
|
||||
const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;
|
||||
fdEntry_t* slab = NULL;
|
||||
assert(rootindex < fdOverflowTableLen);
|
||||
assert(slabindex < fdOverflowTableSlabSize);
|
||||
pthread_mutex_lock(&fdOverflowTableLock);
|
||||
/* Allocate new slab in overflow table if needed */
|
||||
if (fdOverflowTable[rootindex] == NULL) {
|
||||
fdEntry_t* const newSlab =
|
||||
(fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));
|
||||
if (newSlab == NULL) {
|
||||
fprintf(stderr, "Unable to allocate file descriptor overflow"
|
||||
" table slab - out of memory");
|
||||
pthread_mutex_unlock(&fdOverflowTableLock);
|
||||
abort();
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < fdOverflowTableSlabSize; i ++) {
|
||||
pthread_mutex_init(&newSlab[i].lock, NULL);
|
||||
}
|
||||
fdOverflowTable[rootindex] = newSlab;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&fdOverflowTableLock);
|
||||
slab = fdOverflowTable[rootindex];
|
||||
result = &slab[slabindex];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Start a blocking operation :-
|
||||
* Insert thread onto thread list for the fd.
|
||||
*/
|
||||
static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)
|
||||
{
|
||||
self->thr = pthread_self();
|
||||
self->intr = 0;
|
||||
|
||||
pthread_mutex_lock(&(fdEntry->lock));
|
||||
{
|
||||
self->next = fdEntry->threads;
|
||||
fdEntry->threads = self;
|
||||
}
|
||||
pthread_mutex_unlock(&(fdEntry->lock));
|
||||
}
|
||||
|
||||
/*
|
||||
* End a blocking operation :-
|
||||
* Remove thread from thread list for the fd
|
||||
* If fd has been interrupted then set errno to EBADF
|
||||
*/
|
||||
static inline void endOp
|
||||
(fdEntry_t *fdEntry, threadEntry_t *self)
|
||||
{
|
||||
int orig_errno = errno;
|
||||
pthread_mutex_lock(&(fdEntry->lock));
|
||||
{
|
||||
threadEntry_t *curr, *prev=NULL;
|
||||
curr = fdEntry->threads;
|
||||
while (curr != NULL) {
|
||||
if (curr == self) {
|
||||
if (curr->intr) {
|
||||
orig_errno = EBADF;
|
||||
}
|
||||
if (prev == NULL) {
|
||||
fdEntry->threads = curr->next;
|
||||
} else {
|
||||
prev->next = curr->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&(fdEntry->lock));
|
||||
errno = orig_errno;
|
||||
}
|
||||
|
||||
/************** Basic I/O operations here ***************/
|
||||
|
||||
/*
|
||||
* Macro to perform a blocking IO operation. Restarts
|
||||
* automatically if interrupted by signal (other than
|
||||
* our wakeup signal)
|
||||
*/
|
||||
#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \
|
||||
int ret; \
|
||||
threadEntry_t self; \
|
||||
fdEntry_t *fdEntry = getFdEntry(FD); \
|
||||
if (fdEntry == NULL) { \
|
||||
errno = EBADF; \
|
||||
return -1; \
|
||||
} \
|
||||
do { \
|
||||
startOp(fdEntry, &self); \
|
||||
ret = FUNC; \
|
||||
endOp(fdEntry, &self); \
|
||||
} while (ret == -1 && errno == EINTR); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
|
||||
int NET_Connect(int s, struct sockaddr *addr, int addrlen) {
|
||||
int crc = -1, prc = -1;
|
||||
threadEntry_t self;
|
||||
fdEntry_t* fdEntry = getFdEntry(s);
|
||||
|
||||
if (fdEntry == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* On AIX, when the system call connect() is interrupted, the connection
|
||||
* is not aborted and it will be established asynchronously by the kernel.
|
||||
* Hence, no need to restart connect() when EINTR is received
|
||||
*/
|
||||
startOp(fdEntry, &self);
|
||||
crc = connect(s, addr, addrlen);
|
||||
endOp(fdEntry, &self);
|
||||
|
||||
if (crc == -1 && errno == EINTR) {
|
||||
struct pollfd s_pollfd;
|
||||
int sockopt_arg = 0;
|
||||
socklen_t len;
|
||||
|
||||
s_pollfd.fd = s;
|
||||
s_pollfd.events = POLLOUT | POLLERR;
|
||||
|
||||
/* poll the file descriptor */
|
||||
do {
|
||||
startOp(fdEntry, &self);
|
||||
prc = poll(&s_pollfd, 1, -1);
|
||||
endOp(fdEntry, &self);
|
||||
} while (prc == -1 && errno == EINTR);
|
||||
|
||||
if (prc < 0)
|
||||
return prc;
|
||||
|
||||
len = sizeof(sockopt_arg);
|
||||
|
||||
/* Check whether the connection has been established */
|
||||
if (getsockopt(s, SOL_SOCKET, SO_ERROR, &sockopt_arg, &len) == -1)
|
||||
return -1;
|
||||
|
||||
if (sockopt_arg != 0 ) {
|
||||
errno = sockopt_arg;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* At this point, fd is connected. Set successful return code */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {
|
||||
BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );
|
||||
}
|
|
@ -1,295 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2022, 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 <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include "jvm.h"
|
||||
#include "net_util.h"
|
||||
|
||||
/*
|
||||
* Stack allocated by thread when doing blocking operation
|
||||
*/
|
||||
typedef struct threadEntry {
|
||||
pthread_t thr; /* this thread */
|
||||
struct threadEntry *next; /* next thread */
|
||||
int intr; /* interrupted */
|
||||
} threadEntry_t;
|
||||
|
||||
/*
|
||||
* Heap allocated during initialized - one entry per fd
|
||||
*/
|
||||
typedef struct {
|
||||
pthread_mutex_t lock; /* fd lock */
|
||||
threadEntry_t *threads; /* threads blocked on fd */
|
||||
} fdEntry_t;
|
||||
|
||||
/*
|
||||
* Signal to unblock thread
|
||||
*/
|
||||
#define WAKEUP_SIGNAL (SIGRTMAX - 2)
|
||||
|
||||
/*
|
||||
* fdTable holds one entry per file descriptor, up to a certain
|
||||
* maximum.
|
||||
* Theoretically, the number of possible file descriptors can get
|
||||
* large, though usually it does not. Entries for small value file
|
||||
* descriptors are kept in a simple table, which covers most scenarios.
|
||||
* Entries for large value file descriptors are kept in an overflow
|
||||
* table, which is organized as a sparse two dimensional array whose
|
||||
* slabs are allocated on demand. This covers all corner cases while
|
||||
* keeping memory consumption reasonable.
|
||||
*/
|
||||
|
||||
/* Base table for low value file descriptors */
|
||||
static fdEntry_t* fdTable = NULL;
|
||||
/* Maximum size of base table (in number of entries). */
|
||||
static const int fdTableMaxSize = 0x1000; /* 4K */
|
||||
/* Actual size of base table (in number of entries) */
|
||||
static int fdTableLen = 0;
|
||||
/* Max. theoretical number of file descriptors on system. */
|
||||
static int fdLimit = 0;
|
||||
|
||||
/* Overflow table, should base table not be large enough. Organized as
|
||||
* an array of n slabs, each holding 64k entries.
|
||||
*/
|
||||
static fdEntry_t** fdOverflowTable = NULL;
|
||||
/* Number of slabs in the overflow table */
|
||||
static int fdOverflowTableLen = 0;
|
||||
/* Number of entries in one slab */
|
||||
static const int fdOverflowTableSlabSize = 0x10000; /* 64k */
|
||||
pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Null signal handler
|
||||
*/
|
||||
static void sig_wakeup(int sig) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization routine (executed when library is loaded)
|
||||
* Allocate fd tables and sets up signal handler.
|
||||
*/
|
||||
static void __attribute((constructor)) init() {
|
||||
struct rlimit nbr_files;
|
||||
sigset_t sigset;
|
||||
struct sigaction sa;
|
||||
int i = 0;
|
||||
|
||||
/* Determine the maximum number of possible file descriptors. */
|
||||
if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to get max # of allocated fds\n");
|
||||
abort();
|
||||
}
|
||||
if (nbr_files.rlim_max != RLIM_INFINITY) {
|
||||
fdLimit = nbr_files.rlim_max;
|
||||
} else {
|
||||
/* We just do not know. */
|
||||
fdLimit = INT_MAX;
|
||||
}
|
||||
|
||||
/* Allocate table for low value file descriptors. */
|
||||
fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;
|
||||
fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));
|
||||
if (fdTable == NULL) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to allocate file descriptor table - out of memory");
|
||||
abort();
|
||||
} else {
|
||||
for (i = 0; i < fdTableLen; i ++) {
|
||||
pthread_mutex_init(&fdTable[i].lock, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate overflow table, if needed */
|
||||
if (fdLimit > fdTableMaxSize) {
|
||||
fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;
|
||||
fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));
|
||||
if (fdOverflowTable == NULL) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to allocate file descriptor overflow table - out of memory");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the signal handler
|
||||
*/
|
||||
sa.sa_handler = sig_wakeup;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(WAKEUP_SIGNAL, &sa, NULL);
|
||||
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, WAKEUP_SIGNAL);
|
||||
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the fd table for this fd.
|
||||
*/
|
||||
static inline fdEntry_t *getFdEntry(int fd)
|
||||
{
|
||||
fdEntry_t* result = NULL;
|
||||
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This should not happen. If it does, our assumption about
|
||||
* max. fd value was wrong. */
|
||||
assert(fd < fdLimit);
|
||||
|
||||
if (fd < fdTableMaxSize) {
|
||||
/* fd is in base table. */
|
||||
assert(fd < fdTableLen);
|
||||
result = &fdTable[fd];
|
||||
} else {
|
||||
/* fd is in overflow table. */
|
||||
const int indexInOverflowTable = fd - fdTableMaxSize;
|
||||
const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;
|
||||
const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;
|
||||
fdEntry_t* slab = NULL;
|
||||
assert(rootindex < fdOverflowTableLen);
|
||||
assert(slabindex < fdOverflowTableSlabSize);
|
||||
pthread_mutex_lock(&fdOverflowTableLock);
|
||||
/* Allocate new slab in overflow table if needed */
|
||||
if (fdOverflowTable[rootindex] == NULL) {
|
||||
fdEntry_t* const newSlab =
|
||||
(fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));
|
||||
if (newSlab == NULL) {
|
||||
fprintf(stderr, "Unable to allocate file descriptor overflow"
|
||||
" table slab - out of memory");
|
||||
pthread_mutex_unlock(&fdOverflowTableLock);
|
||||
abort();
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < fdOverflowTableSlabSize; i ++) {
|
||||
pthread_mutex_init(&newSlab[i].lock, NULL);
|
||||
}
|
||||
fdOverflowTable[rootindex] = newSlab;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&fdOverflowTableLock);
|
||||
slab = fdOverflowTable[rootindex];
|
||||
result = &slab[slabindex];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a blocking operation :-
|
||||
* Insert thread onto thread list for the fd.
|
||||
*/
|
||||
static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)
|
||||
{
|
||||
self->thr = pthread_self();
|
||||
self->intr = 0;
|
||||
|
||||
pthread_mutex_lock(&(fdEntry->lock));
|
||||
{
|
||||
self->next = fdEntry->threads;
|
||||
fdEntry->threads = self;
|
||||
}
|
||||
pthread_mutex_unlock(&(fdEntry->lock));
|
||||
}
|
||||
|
||||
/*
|
||||
* End a blocking operation :-
|
||||
* Remove thread from thread list for the fd
|
||||
* If fd has been interrupted then set errno to EBADF
|
||||
*/
|
||||
static inline void endOp
|
||||
(fdEntry_t *fdEntry, threadEntry_t *self)
|
||||
{
|
||||
int orig_errno = errno;
|
||||
pthread_mutex_lock(&(fdEntry->lock));
|
||||
{
|
||||
threadEntry_t *curr, *prev=NULL;
|
||||
curr = fdEntry->threads;
|
||||
while (curr != NULL) {
|
||||
if (curr == self) {
|
||||
if (curr->intr) {
|
||||
orig_errno = EBADF;
|
||||
}
|
||||
if (prev == NULL) {
|
||||
fdEntry->threads = curr->next;
|
||||
} else {
|
||||
prev->next = curr->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&(fdEntry->lock));
|
||||
errno = orig_errno;
|
||||
}
|
||||
|
||||
/************** Basic I/O operations here ***************/
|
||||
|
||||
/*
|
||||
* Macro to perform a blocking IO operation. Restarts
|
||||
* automatically if interrupted by signal (other than
|
||||
* our wakeup signal)
|
||||
*/
|
||||
#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \
|
||||
int ret; \
|
||||
threadEntry_t self; \
|
||||
fdEntry_t *fdEntry = getFdEntry(FD); \
|
||||
if (fdEntry == NULL) { \
|
||||
errno = EBADF; \
|
||||
return -1; \
|
||||
} \
|
||||
do { \
|
||||
startOp(fdEntry, &self); \
|
||||
ret = FUNC; \
|
||||
endOp(fdEntry, &self); \
|
||||
} while (ret == -1 && errno == EINTR); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
int NET_Connect(int s, struct sockaddr *addr, int addrlen) {
|
||||
BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );
|
||||
}
|
||||
|
||||
int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {
|
||||
BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );
|
||||
}
|
|
@ -1,298 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2022, 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 <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include "jvm.h"
|
||||
#include "net_util.h"
|
||||
|
||||
/*
|
||||
* Stack allocated by thread when doing blocking operation
|
||||
*/
|
||||
typedef struct threadEntry {
|
||||
pthread_t thr; /* this thread */
|
||||
struct threadEntry *next; /* next thread */
|
||||
int intr; /* interrupted */
|
||||
} threadEntry_t;
|
||||
|
||||
/*
|
||||
* Heap allocated during initialized - one entry per fd
|
||||
*/
|
||||
typedef struct {
|
||||
pthread_mutex_t lock; /* fd lock */
|
||||
threadEntry_t *threads; /* threads blocked on fd */
|
||||
} fdEntry_t;
|
||||
|
||||
/*
|
||||
* Signal to unblock thread
|
||||
*/
|
||||
static int sigWakeup = SIGIO;
|
||||
|
||||
/*
|
||||
* fdTable holds one entry per file descriptor, up to a certain
|
||||
* maximum.
|
||||
* Theoretically, the number of possible file descriptors can get
|
||||
* large, though usually it does not. Entries for small value file
|
||||
* descriptors are kept in a simple table, which covers most scenarios.
|
||||
* Entries for large value file descriptors are kept in an overflow
|
||||
* table, which is organized as a sparse two dimensional array whose
|
||||
* slabs are allocated on demand. This covers all corner cases while
|
||||
* keeping memory consumption reasonable.
|
||||
*/
|
||||
|
||||
/* Base table for low value file descriptors */
|
||||
static fdEntry_t* fdTable = NULL;
|
||||
/* Maximum size of base table (in number of entries). */
|
||||
static const int fdTableMaxSize = 0x1000; /* 4K */
|
||||
/* Actual size of base table (in number of entries) */
|
||||
static int fdTableLen = 0;
|
||||
/* Max. theoretical number of file descriptors on system. */
|
||||
static int fdLimit = 0;
|
||||
|
||||
/* Overflow table, should base table not be large enough. Organized as
|
||||
* an array of n slabs, each holding 64k entries.
|
||||
*/
|
||||
static fdEntry_t** fdOverflowTable = NULL;
|
||||
/* Number of slabs in the overflow table */
|
||||
static int fdOverflowTableLen = 0;
|
||||
/* Number of entries in one slab */
|
||||
static const int fdOverflowTableSlabSize = 0x10000; /* 64k */
|
||||
pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Null signal handler
|
||||
*/
|
||||
static void sig_wakeup(int sig) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization routine (executed when library is loaded)
|
||||
* Allocate fd tables and sets up signal handler.
|
||||
*/
|
||||
static void __attribute((constructor)) init() {
|
||||
struct rlimit nbr_files;
|
||||
sigset_t sigset;
|
||||
struct sigaction sa;
|
||||
int i = 0;
|
||||
|
||||
/* Determine the maximum number of possible file descriptors. */
|
||||
if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to get max # of allocated fds\n");
|
||||
abort();
|
||||
}
|
||||
if (nbr_files.rlim_max != RLIM_INFINITY) {
|
||||
fdLimit = nbr_files.rlim_max;
|
||||
} else {
|
||||
/* We just do not know. */
|
||||
fdLimit = INT_MAX;
|
||||
}
|
||||
|
||||
/* Allocate table for low value file descriptors. */
|
||||
fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;
|
||||
fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));
|
||||
if (fdTable == NULL) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to allocate file descriptor table - out of memory");
|
||||
abort();
|
||||
} else {
|
||||
for (i = 0; i < fdTableLen; i ++) {
|
||||
pthread_mutex_init(&fdTable[i].lock, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate overflow table, if needed */
|
||||
if (fdLimit > fdTableMaxSize) {
|
||||
fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;
|
||||
fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));
|
||||
if (fdOverflowTable == NULL) {
|
||||
fprintf(stderr, "library initialization failed - "
|
||||
"unable to allocate file descriptor overflow table - out of memory");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the signal handler
|
||||
*/
|
||||
sa.sa_handler = sig_wakeup;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(sigWakeup, &sa, NULL);
|
||||
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, sigWakeup);
|
||||
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the fd table for this fd.
|
||||
*/
|
||||
static inline fdEntry_t *getFdEntry(int fd)
|
||||
{
|
||||
fdEntry_t* result = NULL;
|
||||
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This should not happen. If it does, our assumption about
|
||||
* max. fd value was wrong. */
|
||||
assert(fd < fdLimit);
|
||||
|
||||
if (fd < fdTableMaxSize) {
|
||||
/* fd is in base table. */
|
||||
assert(fd < fdTableLen);
|
||||
result = &fdTable[fd];
|
||||
} else {
|
||||
/* fd is in overflow table. */
|
||||
const int indexInOverflowTable = fd - fdTableMaxSize;
|
||||
const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;
|
||||
const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;
|
||||
fdEntry_t* slab = NULL;
|
||||
assert(rootindex < fdOverflowTableLen);
|
||||
assert(slabindex < fdOverflowTableSlabSize);
|
||||
pthread_mutex_lock(&fdOverflowTableLock);
|
||||
/* Allocate new slab in overflow table if needed */
|
||||
if (fdOverflowTable[rootindex] == NULL) {
|
||||
fdEntry_t* const newSlab =
|
||||
(fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));
|
||||
if (newSlab == NULL) {
|
||||
fprintf(stderr, "Unable to allocate file descriptor overflow"
|
||||
" table slab - out of memory");
|
||||
pthread_mutex_unlock(&fdOverflowTableLock);
|
||||
abort();
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < fdOverflowTableSlabSize; i ++) {
|
||||
pthread_mutex_init(&newSlab[i].lock, NULL);
|
||||
}
|
||||
fdOverflowTable[rootindex] = newSlab;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&fdOverflowTableLock);
|
||||
slab = fdOverflowTable[rootindex];
|
||||
result = &slab[slabindex];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Start a blocking operation :-
|
||||
* Insert thread onto thread list for the fd.
|
||||
*/
|
||||
static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)
|
||||
{
|
||||
self->thr = pthread_self();
|
||||
self->intr = 0;
|
||||
|
||||
pthread_mutex_lock(&(fdEntry->lock));
|
||||
{
|
||||
self->next = fdEntry->threads;
|
||||
fdEntry->threads = self;
|
||||
}
|
||||
pthread_mutex_unlock(&(fdEntry->lock));
|
||||
}
|
||||
|
||||
/*
|
||||
* End a blocking operation :-
|
||||
* Remove thread from thread list for the fd
|
||||
* If fd has been interrupted then set errno to EBADF
|
||||
*/
|
||||
static inline void endOp
|
||||
(fdEntry_t *fdEntry, threadEntry_t *self)
|
||||
{
|
||||
int orig_errno = errno;
|
||||
pthread_mutex_lock(&(fdEntry->lock));
|
||||
{
|
||||
threadEntry_t *curr, *prev=NULL;
|
||||
curr = fdEntry->threads;
|
||||
while (curr != NULL) {
|
||||
if (curr == self) {
|
||||
if (curr->intr) {
|
||||
orig_errno = EBADF;
|
||||
}
|
||||
if (prev == NULL) {
|
||||
fdEntry->threads = curr->next;
|
||||
} else {
|
||||
prev->next = curr->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&(fdEntry->lock));
|
||||
errno = orig_errno;
|
||||
}
|
||||
|
||||
/************** Basic I/O operations here ***************/
|
||||
|
||||
/*
|
||||
* Macro to perform a blocking IO operation. Restarts
|
||||
* automatically if interrupted by signal (other than
|
||||
* our wakeup signal)
|
||||
*/
|
||||
#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \
|
||||
int ret; \
|
||||
threadEntry_t self; \
|
||||
fdEntry_t *fdEntry = getFdEntry(FD); \
|
||||
if (fdEntry == NULL) { \
|
||||
errno = EBADF; \
|
||||
return -1; \
|
||||
} \
|
||||
do { \
|
||||
startOp(fdEntry, &self); \
|
||||
ret = FUNC; \
|
||||
endOp(fdEntry, &self); \
|
||||
} while (ret == -1 && errno == EINTR); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
int NET_Connect(int s, struct sockaddr *addr, int addrlen) {
|
||||
BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );
|
||||
}
|
||||
|
||||
int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {
|
||||
BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );
|
||||
}
|
|
@ -81,7 +81,6 @@ DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
|
|||
|
||||
/* check if SO_REUSEPORT is supported on this platform */
|
||||
REUSEPORT_available = reuseport_supported(IPv6_available);
|
||||
platformInit();
|
||||
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
|
|
|
@ -142,8 +142,6 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
|
|||
JNIEXPORT jobject JNICALL
|
||||
NET_SockaddrToInetAddress(JNIEnv *env, SOCKETADDRESS *sa, int *port);
|
||||
|
||||
void platformInit();
|
||||
|
||||
JNIEXPORT jint JNICALL NET_GetPortFromSockaddr(SOCKETADDRESS *sa);
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
|
|
|
@ -279,7 +279,7 @@ tcp_ping4(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout,
|
|||
SET_NONBLOCKING(fd);
|
||||
|
||||
sa->sa4.sin_port = htons(7); // echo port
|
||||
connect_rv = NET_Connect(fd, &sa->sa, sizeof(struct sockaddr_in));
|
||||
connect_rv = connect(fd, &sa->sa, sizeof(struct sockaddr_in));
|
||||
|
||||
// connection established or refused immediately, either way it means
|
||||
// we were able to reach the host!
|
||||
|
|
|
@ -479,7 +479,7 @@ tcp_ping6(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout,
|
|||
SET_NONBLOCKING(fd);
|
||||
|
||||
sa->sa6.sin6_port = htons(7); // echo port
|
||||
connect_rv = NET_Connect(fd, &sa->sa, sizeof(struct sockaddr_in6));
|
||||
connect_rv = connect(fd, &sa->sa, sizeof(struct sockaddr_in6));
|
||||
|
||||
// connection established or refused immediately, either way it means
|
||||
// we were able to reach the host!
|
||||
|
|
|
@ -224,21 +224,6 @@ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env,
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(_AIX)
|
||||
|
||||
/* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */
|
||||
extern void aix_close_init();
|
||||
|
||||
void platformInit () {
|
||||
aix_close_init();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void platformInit () {}
|
||||
|
||||
#endif
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
NET_EnableFastTcpLoopback(int fd) {
|
||||
return 0;
|
||||
|
@ -715,7 +700,7 @@ NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout)
|
|||
pfd.events |= POLLOUT;
|
||||
|
||||
errno = 0;
|
||||
read_rv = NET_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
|
||||
read_rv = poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
|
||||
|
||||
newNanoTime = JVM_NanoTime(env, 0);
|
||||
nanoTimeout -= (newNanoTime - prevNanoTime);
|
||||
|
|
|
@ -74,10 +74,6 @@ typedef union {
|
|||
/************************************************************************
|
||||
* Functions
|
||||
*/
|
||||
|
||||
int NET_Connect(int s, struct sockaddr *addr, int addrlen);
|
||||
int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout);
|
||||
|
||||
void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env,
|
||||
const char* hostname,
|
||||
int gai_error);
|
||||
|
|
|
@ -126,8 +126,6 @@ DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
void platformInit() {}
|
||||
|
||||
/*
|
||||
* Since winsock doesn't have the equivalent of strerror(errno)
|
||||
* use table to lookup error text for the error.
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2019, 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 4796166
|
||||
* @library /test/lib
|
||||
* @summary Linger interval delays usage of released file descriptor
|
||||
* @run main LingerTest
|
||||
* @run main/othervm -Djava.net.preferIPv4Stack=true LingerTest
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import jdk.test.lib.net.IPSupport;
|
||||
|
||||
public class LingerTest {
|
||||
|
||||
static class Sender implements Runnable {
|
||||
Socket s;
|
||||
|
||||
public Sender(Socket s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
System.out.println ("Sender starts");
|
||||
try {
|
||||
s.getOutputStream().write(new byte[128*1024]);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
}
|
||||
System.out.println ("Sender ends");
|
||||
}
|
||||
}
|
||||
|
||||
static class Closer implements Runnable {
|
||||
Socket s;
|
||||
|
||||
public Closer(Socket s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
System.out.println ("Closer starts");
|
||||
try {
|
||||
s.close();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
}
|
||||
System.out.println ("Closer ends");
|
||||
}
|
||||
}
|
||||
|
||||
static class Other implements Runnable {
|
||||
final InetAddress address;
|
||||
final int port;
|
||||
final long delay;
|
||||
boolean connected = false;
|
||||
|
||||
public Other(InetAddress address, int port, long delay) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
System.out.println ("Other starts: sleep " + delay);
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
System.out.println ("Other opening socket");
|
||||
Socket s = new Socket(address, port);
|
||||
synchronized (this) {
|
||||
connected = true;
|
||||
}
|
||||
s.close();
|
||||
}
|
||||
catch (Exception ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
System.out.println ("Other ends");
|
||||
}
|
||||
|
||||
public synchronized boolean connected() {
|
||||
return connected;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
IPSupport.throwSkippedExceptionIfNonOperational();
|
||||
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
ServerSocket ss = new ServerSocket(0, 50, loopback);
|
||||
|
||||
Socket s1 = new Socket(loopback, ss.getLocalPort());
|
||||
Socket s2 = ss.accept();
|
||||
|
||||
// setup conditions for untransmitted data and lengthy
|
||||
// linger interval
|
||||
s1.setSendBufferSize(128*1024);
|
||||
s1.setSoLinger(true, 30);
|
||||
s2.setReceiveBufferSize(1*1024);
|
||||
|
||||
// start sender
|
||||
Thread senderThread = new Thread(new Sender(s1));
|
||||
senderThread.start();
|
||||
|
||||
// other thread that will connect after 5 seconds.
|
||||
Other other = new Other(loopback, ss.getLocalPort(), 5000);
|
||||
Thread otherThread = new Thread(other);
|
||||
otherThread.start();
|
||||
|
||||
// give sender time to queue the data
|
||||
System.out.println ("Main sleep 1000");
|
||||
Thread.sleep(1000);
|
||||
System.out.println ("Main continue");
|
||||
|
||||
// close the socket asynchronously
|
||||
Thread closerThread = new Thread(new Closer(s1));
|
||||
closerThread.start();
|
||||
|
||||
System.out.println ("Main sleep 15000");
|
||||
// give other time to run
|
||||
Thread.sleep(15000);
|
||||
System.out.println ("Main closing serversocket");
|
||||
|
||||
ss.close();
|
||||
// check that other is done
|
||||
if (!other.connected()) {
|
||||
throw new RuntimeException("Other thread is blocked");
|
||||
}
|
||||
|
||||
// await termination of all test related threads
|
||||
senderThread.join(60_000);
|
||||
otherThread.join(60_000);
|
||||
closerThread.join(60_000);
|
||||
|
||||
System.out.println ("Main ends");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue