Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/native/java/net/bsd_close.c
32287 views
/*1* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <assert.h>26#include <limits.h>27#include <stdio.h>28#include <stdlib.h>29#include <sys/param.h>30#include <signal.h>31#include <pthread.h>32#include <sys/types.h>33#include <sys/socket.h>34#include <sys/select.h>35#include <sys/time.h>36#include <sys/resource.h>37#include <sys/uio.h>38#include <unistd.h>39#include <errno.h>40#include <sys/poll.h>4142/*43* Stack allocated by thread when doing blocking operation44*/45typedef struct threadEntry {46pthread_t thr; /* this thread */47struct threadEntry *next; /* next thread */48int intr; /* interrupted */49} threadEntry_t;5051/*52* Heap allocated during initialized - one entry per fd53*/54typedef struct {55pthread_mutex_t lock; /* fd lock */56threadEntry_t *threads; /* threads blocked on fd */57} fdEntry_t;5859/*60* Signal to unblock thread61*/62static int sigWakeup = SIGIO;6364/*65* fdTable holds one entry per file descriptor, up to a certain66* maximum.67* Theoretically, the number of possible file descriptors can get68* large, though usually it does not. Entries for small value file69* descriptors are kept in a simple table, which covers most scenarios.70* Entries for large value file descriptors are kept in an overflow71* table, which is organized as a sparse two dimensional array whose72* slabs are allocated on demand. This covers all corner cases while73* keeping memory consumption reasonable.74*/7576/* Base table for low value file descriptors */77static fdEntry_t* fdTable = NULL;78/* Maximum size of base table (in number of entries). */79static const int fdTableMaxSize = 0x1000; /* 4K */80/* Actual size of base table (in number of entries) */81static int fdTableLen = 0;82/* Max. theoretical number of file descriptors on system. */83static int fdLimit = 0;8485/* Overflow table, should base table not be large enough. Organized as86* an array of n slabs, each holding 64k entries.87*/88static fdEntry_t** fdOverflowTable = NULL;89/* Number of slabs in the overflow table */90static int fdOverflowTableLen = 0;91/* Number of entries in one slab */92static const int fdOverflowTableSlabSize = 0x10000; /* 64k */93pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;9495/*96* Null signal handler97*/98static void sig_wakeup(int sig) {99}100101/*102* Initialization routine (executed when library is loaded)103* Allocate fd tables and sets up signal handler.104*/105static void __attribute((constructor)) init() {106struct rlimit nbr_files;107sigset_t sigset;108struct sigaction sa;109int i = 0;110111/* Determine the maximum number of possible file descriptors. */112if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {113fprintf(stderr, "library initialization failed - "114"unable to get max # of allocated fds\n");115abort();116}117if (nbr_files.rlim_max != RLIM_INFINITY) {118fdLimit = nbr_files.rlim_max;119} else {120/* We just do not know. */121fdLimit = INT_MAX;122}123124/* Allocate table for low value file descriptors. */125fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;126fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));127if (fdTable == NULL) {128fprintf(stderr, "library initialization failed - "129"unable to allocate file descriptor table - out of memory");130abort();131} else {132for (i = 0; i < fdTableLen; i ++) {133pthread_mutex_init(&fdTable[i].lock, NULL);134}135}136137/* Allocate overflow table, if needed */138if (fdLimit > fdTableMaxSize) {139fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;140fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));141if (fdOverflowTable == NULL) {142fprintf(stderr, "library initialization failed - "143"unable to allocate file descriptor overflow table - out of memory");144abort();145}146}147148/*149* Setup the signal handler150*/151sa.sa_handler = sig_wakeup;152sa.sa_flags = 0;153sigemptyset(&sa.sa_mask);154sigaction(sigWakeup, &sa, NULL);155156sigemptyset(&sigset);157sigaddset(&sigset, sigWakeup);158sigprocmask(SIG_UNBLOCK, &sigset, NULL);159}160161/*162* Return the fd table for this fd.163*/164static inline fdEntry_t *getFdEntry(int fd)165{166fdEntry_t* result = NULL;167168if (fd < 0) {169return NULL;170}171172/* This should not happen. If it does, our assumption about173* max. fd value was wrong. */174assert(fd < fdLimit);175176if (fd < fdTableMaxSize) {177/* fd is in base table. */178assert(fd < fdTableLen);179result = &fdTable[fd];180} else {181/* fd is in overflow table. */182const int indexInOverflowTable = fd - fdTableMaxSize;183const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;184const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;185fdEntry_t* slab = NULL;186assert(rootindex < fdOverflowTableLen);187assert(slabindex < fdOverflowTableSlabSize);188pthread_mutex_lock(&fdOverflowTableLock);189/* Allocate new slab in overflow table if needed */190if (fdOverflowTable[rootindex] == NULL) {191fdEntry_t* const newSlab =192(fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));193if (newSlab == NULL) {194fprintf(stderr, "Unable to allocate file descriptor overflow"195" table slab - out of memory");196pthread_mutex_unlock(&fdOverflowTableLock);197abort();198} else {199int i;200for (i = 0; i < fdOverflowTableSlabSize; i ++) {201pthread_mutex_init(&newSlab[i].lock, NULL);202}203fdOverflowTable[rootindex] = newSlab;204}205}206pthread_mutex_unlock(&fdOverflowTableLock);207slab = fdOverflowTable[rootindex];208result = &slab[slabindex];209}210211return result;212213}214215216/*217* Start a blocking operation :-218* Insert thread onto thread list for the fd.219*/220static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)221{222self->thr = pthread_self();223self->intr = 0;224225pthread_mutex_lock(&(fdEntry->lock));226{227self->next = fdEntry->threads;228fdEntry->threads = self;229}230pthread_mutex_unlock(&(fdEntry->lock));231}232233/*234* End a blocking operation :-235* Remove thread from thread list for the fd236* If fd has been interrupted then set errno to EBADF237*/238static inline void endOp239(fdEntry_t *fdEntry, threadEntry_t *self)240{241int orig_errno = errno;242pthread_mutex_lock(&(fdEntry->lock));243{244threadEntry_t *curr, *prev=NULL;245curr = fdEntry->threads;246while (curr != NULL) {247if (curr == self) {248if (curr->intr) {249orig_errno = EBADF;250}251if (prev == NULL) {252fdEntry->threads = curr->next;253} else {254prev->next = curr->next;255}256break;257}258prev = curr;259curr = curr->next;260}261}262pthread_mutex_unlock(&(fdEntry->lock));263errno = orig_errno;264}265266/*267* Close or dup2 a file descriptor ensuring that all threads blocked on268* the file descriptor are notified via a wakeup signal.269*270* fd1 < 0 => close(fd2)271* fd1 >= 0 => dup2(fd1, fd2)272*273* Returns -1 with errno set if operation fails.274*/275static int closefd(int fd1, int fd2) {276int rv, orig_errno;277fdEntry_t *fdEntry = getFdEntry(fd2);278if (fdEntry == NULL) {279errno = EBADF;280return -1;281}282283/*284* Lock the fd to hold-off additional I/O on this fd.285*/286pthread_mutex_lock(&(fdEntry->lock));287288{289/*290* Send a wakeup signal to all threads blocked on this291* file descriptor.292*/293threadEntry_t *curr = fdEntry->threads;294while (curr != NULL) {295curr->intr = 1;296pthread_kill( curr->thr, sigWakeup );297curr = curr->next;298}299300/*301* And close/dup the file descriptor302* (restart if interrupted by signal)303*/304do {305if (fd1 < 0) {306rv = close(fd2);307} else {308rv = dup2(fd1, fd2);309}310} while (rv == -1 && errno == EINTR);311312}313314/*315* Unlock without destroying errno316*/317orig_errno = errno;318pthread_mutex_unlock(&(fdEntry->lock));319errno = orig_errno;320321return rv;322}323324/*325* Wrapper for dup2 - same semantics as dup2 system call except326* that any threads blocked in an I/O system call on fd2 will be327* preempted and return -1/EBADF;328*/329int NET_Dup2(int fd, int fd2) {330if (fd < 0) {331errno = EBADF;332return -1;333}334return closefd(fd, fd2);335}336337/*338* Wrapper for close - same semantics as close system call339* except that any threads blocked in an I/O on fd will be340* preempted and the I/O system call will return -1/EBADF.341*/342int NET_SocketClose(int fd) {343return closefd(-1, fd);344}345346/************** Basic I/O operations here ***************/347348/*349* Macro to perform a blocking IO operation. Restarts350* automatically if interrupted by signal (other than351* our wakeup signal)352*/353#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \354int ret; \355threadEntry_t self; \356fdEntry_t *fdEntry = getFdEntry(FD); \357if (fdEntry == NULL) { \358errno = EBADF; \359return -1; \360} \361do { \362startOp(fdEntry, &self); \363ret = FUNC; \364endOp(fdEntry, &self); \365} while (ret == -1 && errno == EINTR); \366return ret; \367}368369int NET_Read(int s, void* buf, size_t len) {370BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) );371}372373int NET_NonBlockingRead(int s, void* buf, size_t len) {374BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, MSG_DONTWAIT));375}376377int NET_ReadV(int s, const struct iovec * vector, int count) {378BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) );379}380381int NET_RecvFrom(int s, void *buf, int len, unsigned int flags,382struct sockaddr *from, int *fromlen) {383/* casting int *fromlen -> socklen_t* Both are ints */384BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, (socklen_t *)fromlen) );385}386387int NET_Send(int s, void *msg, int len, unsigned int flags) {388BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) );389}390391int NET_WriteV(int s, const struct iovec * vector, int count) {392BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) );393}394395int NET_SendTo(int s, const void *msg, int len, unsigned int396flags, const struct sockaddr *to, int tolen) {397BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) );398}399400int NET_Accept(int s, struct sockaddr *addr, int *addrlen) {401socklen_t len = *addrlen;402int error = accept(s, addr, &len);403if (error != -1)404*addrlen = (int)len;405BLOCKING_IO_RETURN_INT( s, error );406}407408int NET_Connect(int s, struct sockaddr *addr, int addrlen) {409BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );410}411412#ifndef USE_SELECT413int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {414BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );415}416#else417int NET_Select(int s, fd_set *readfds, fd_set *writefds,418fd_set *exceptfds, struct timeval *timeout) {419BLOCKING_IO_RETURN_INT( s-1,420select(s, readfds, writefds, exceptfds, timeout) );421}422#endif423424/*425* Wrapper for select(s, timeout). We are using select() on Mac OS due to Bug 7131399.426* Auto restarts with adjusted timeout if interrupted by427* signal other than our wakeup signal.428*/429int NET_Timeout0(int s, long timeout, long currentTime) {430long prevtime = currentTime, newtime;431struct timeval t, *tp = &t;432fd_set fds;433fd_set* fdsp = NULL;434int allocated = 0;435threadEntry_t self;436fdEntry_t *fdEntry = getFdEntry(s);437438/*439* Check that fd hasn't been closed.440*/441if (fdEntry == NULL) {442errno = EBADF;443return -1;444}445446/*447* Pick up current time as may need to adjust timeout448*/449if (timeout > 0) {450/* Timed */451t.tv_sec = timeout / 1000;452t.tv_usec = (timeout % 1000) * 1000;453} else if (timeout < 0) {454/* Blocking */455tp = 0;456} else {457/* Poll */458t.tv_sec = 0;459t.tv_usec = 0;460}461462if (s < FD_SETSIZE) {463fdsp = &fds;464FD_ZERO(fdsp);465} else {466int length = (howmany(s+1, NFDBITS)) * sizeof(int);467fdsp = (fd_set *) calloc(1, length);468if (fdsp == NULL) {469return -1; // errno will be set to ENOMEM470}471allocated = 1;472}473FD_SET(s, fdsp);474475for(;;) {476int rv;477478/*479* call select on the fd. If interrupted by our wakeup signal480* errno will be set to EBADF.481*/482483startOp(fdEntry, &self);484rv = select(s+1, fdsp, 0, 0, tp);485endOp(fdEntry, &self);486487/*488* If interrupted then adjust timeout. If timeout489* has expired return 0 (indicating timeout expired).490*/491if (rv < 0 && errno == EINTR) {492if (timeout > 0) {493struct timeval now;494gettimeofday(&now, NULL);495newtime = now.tv_sec * 1000 + now.tv_usec / 1000;496timeout -= newtime - prevtime;497if (timeout <= 0) {498if (allocated != 0)499free(fdsp);500return 0;501}502prevtime = newtime;503t.tv_sec = timeout / 1000;504t.tv_usec = (timeout % 1000) * 1000;505}506} else {507if (allocated != 0)508free(fdsp);509return rv;510}511512}513}514515516