8295792: Clean up old async close code

Reviewed-by: alanb
This commit is contained in:
Daniel Jeliński 2022-10-24 15:25:48 +00:00
parent 5ac6f185ee
commit 2f3f3b6185
12 changed files with 4 additions and 1159 deletions

View file

@ -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, \

View file

@ -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) );
}

View file

@ -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) );
}

View file

@ -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) );
}

View file

@ -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;
}

View file

@ -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

View file

@ -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!

View file

@ -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!

View file

@ -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);

View file

@ -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);

View file

@ -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.

View file

@ -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");
}
}