Path: blob/master/src/java.base/macosx/native/libnet/bsd_close.c
41119 views
/*1* Copyright (c) 2001, 2020, 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 <poll.h>41#include "jvm.h"42#include "net_util.h"4344/*45* Stack allocated by thread when doing blocking operation46*/47typedef struct threadEntry {48pthread_t thr; /* this thread */49struct threadEntry *next; /* next thread */50int intr; /* interrupted */51} threadEntry_t;5253/*54* Heap allocated during initialized - one entry per fd55*/56typedef struct {57pthread_mutex_t lock; /* fd lock */58threadEntry_t *threads; /* threads blocked on fd */59} fdEntry_t;6061/*62* Signal to unblock thread63*/64static int sigWakeup = SIGIO;6566/*67* fdTable holds one entry per file descriptor, up to a certain68* maximum.69* Theoretically, the number of possible file descriptors can get70* large, though usually it does not. Entries for small value file71* descriptors are kept in a simple table, which covers most scenarios.72* Entries for large value file descriptors are kept in an overflow73* table, which is organized as a sparse two dimensional array whose74* slabs are allocated on demand. This covers all corner cases while75* keeping memory consumption reasonable.76*/7778/* Base table for low value file descriptors */79static fdEntry_t* fdTable = NULL;80/* Maximum size of base table (in number of entries). */81static const int fdTableMaxSize = 0x1000; /* 4K */82/* Actual size of base table (in number of entries) */83static int fdTableLen = 0;84/* Max. theoretical number of file descriptors on system. */85static int fdLimit = 0;8687/* Overflow table, should base table not be large enough. Organized as88* an array of n slabs, each holding 64k entries.89*/90static fdEntry_t** fdOverflowTable = NULL;91/* Number of slabs in the overflow table */92static int fdOverflowTableLen = 0;93/* Number of entries in one slab */94static const int fdOverflowTableSlabSize = 0x10000; /* 64k */95pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;9697/*98* Null signal handler99*/100static void sig_wakeup(int sig) {101}102103/*104* Initialization routine (executed when library is loaded)105* Allocate fd tables and sets up signal handler.106*/107static void __attribute((constructor)) init() {108struct rlimit nbr_files;109sigset_t sigset;110struct sigaction sa;111int i = 0;112113/* Determine the maximum number of possible file descriptors. */114if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {115fprintf(stderr, "library initialization failed - "116"unable to get max # of allocated fds\n");117abort();118}119if (nbr_files.rlim_max != RLIM_INFINITY) {120fdLimit = nbr_files.rlim_max;121} else {122/* We just do not know. */123fdLimit = INT_MAX;124}125126/* Allocate table for low value file descriptors. */127fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;128fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));129if (fdTable == NULL) {130fprintf(stderr, "library initialization failed - "131"unable to allocate file descriptor table - out of memory");132abort();133} else {134for (i = 0; i < fdTableLen; i ++) {135pthread_mutex_init(&fdTable[i].lock, NULL);136}137}138139/* Allocate overflow table, if needed */140if (fdLimit > fdTableMaxSize) {141fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;142fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));143if (fdOverflowTable == NULL) {144fprintf(stderr, "library initialization failed - "145"unable to allocate file descriptor overflow table - out of memory");146abort();147}148}149150/*151* Setup the signal handler152*/153sa.sa_handler = sig_wakeup;154sa.sa_flags = 0;155sigemptyset(&sa.sa_mask);156sigaction(sigWakeup, &sa, NULL);157158sigemptyset(&sigset);159sigaddset(&sigset, sigWakeup);160sigprocmask(SIG_UNBLOCK, &sigset, NULL);161}162163/*164* Return the fd table for this fd.165*/166static inline fdEntry_t *getFdEntry(int fd)167{168fdEntry_t* result = NULL;169170if (fd < 0) {171return NULL;172}173174/* This should not happen. If it does, our assumption about175* max. fd value was wrong. */176assert(fd < fdLimit);177178if (fd < fdTableMaxSize) {179/* fd is in base table. */180assert(fd < fdTableLen);181result = &fdTable[fd];182} else {183/* fd is in overflow table. */184const int indexInOverflowTable = fd - fdTableMaxSize;185const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;186const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;187fdEntry_t* slab = NULL;188assert(rootindex < fdOverflowTableLen);189assert(slabindex < fdOverflowTableSlabSize);190pthread_mutex_lock(&fdOverflowTableLock);191/* Allocate new slab in overflow table if needed */192if (fdOverflowTable[rootindex] == NULL) {193fdEntry_t* const newSlab =194(fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));195if (newSlab == NULL) {196fprintf(stderr, "Unable to allocate file descriptor overflow"197" table slab - out of memory");198pthread_mutex_unlock(&fdOverflowTableLock);199abort();200} else {201int i;202for (i = 0; i < fdOverflowTableSlabSize; i ++) {203pthread_mutex_init(&newSlab[i].lock, NULL);204}205fdOverflowTable[rootindex] = newSlab;206}207}208pthread_mutex_unlock(&fdOverflowTableLock);209slab = fdOverflowTable[rootindex];210result = &slab[slabindex];211}212213return result;214215}216217218/*219* Start a blocking operation :-220* Insert thread onto thread list for the fd.221*/222static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)223{224self->thr = pthread_self();225self->intr = 0;226227pthread_mutex_lock(&(fdEntry->lock));228{229self->next = fdEntry->threads;230fdEntry->threads = self;231}232pthread_mutex_unlock(&(fdEntry->lock));233}234235/*236* End a blocking operation :-237* Remove thread from thread list for the fd238* If fd has been interrupted then set errno to EBADF239*/240static inline void endOp241(fdEntry_t *fdEntry, threadEntry_t *self)242{243int orig_errno = errno;244pthread_mutex_lock(&(fdEntry->lock));245{246threadEntry_t *curr, *prev=NULL;247curr = fdEntry->threads;248while (curr != NULL) {249if (curr == self) {250if (curr->intr) {251orig_errno = EBADF;252}253if (prev == NULL) {254fdEntry->threads = curr->next;255} else {256prev->next = curr->next;257}258break;259}260prev = curr;261curr = curr->next;262}263}264pthread_mutex_unlock(&(fdEntry->lock));265errno = orig_errno;266}267268/*269* Close or dup2 a file descriptor ensuring that all threads blocked on270* the file descriptor are notified via a wakeup signal.271*272* fd1 < 0 => close(fd2)273* fd1 >= 0 => dup2(fd1, fd2)274*275* Returns -1 with errno set if operation fails.276*/277static int closefd(int fd1, int fd2) {278int rv, orig_errno;279fdEntry_t *fdEntry = getFdEntry(fd2);280if (fdEntry == NULL) {281errno = EBADF;282return -1;283}284285/*286* Lock the fd to hold-off additional I/O on this fd.287*/288pthread_mutex_lock(&(fdEntry->lock));289290{291/*292* Send a wakeup signal to all threads blocked on this293* file descriptor.294*/295threadEntry_t *curr = fdEntry->threads;296while (curr != NULL) {297curr->intr = 1;298pthread_kill( curr->thr, sigWakeup );299curr = curr->next;300}301302/*303* And close/dup the file descriptor304* (restart if interrupted by signal)305*/306do {307if (fd1 < 0) {308rv = close(fd2);309} else {310rv = dup2(fd1, fd2);311}312} while (rv == -1 && errno == EINTR);313314}315316/*317* Unlock without destroying errno318*/319orig_errno = errno;320pthread_mutex_unlock(&(fdEntry->lock));321errno = orig_errno;322323return rv;324}325326/*327* Wrapper for dup2 - same semantics as dup2 system call except328* that any threads blocked in an I/O system call on fd2 will be329* preempted and return -1/EBADF;330*/331int NET_Dup2(int fd, int fd2) {332if (fd < 0) {333errno = EBADF;334return -1;335}336return closefd(fd, fd2);337}338339/*340* Wrapper for close - same semantics as close system call341* except that any threads blocked in an I/O on fd will be342* preempted and the I/O system call will return -1/EBADF.343*/344int NET_SocketClose(int fd) {345return closefd(-1, fd);346}347348/************** Basic I/O operations here ***************/349350/*351* Macro to perform a blocking IO operation. Restarts352* automatically if interrupted by signal (other than353* our wakeup signal)354*/355#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \356int ret; \357threadEntry_t self; \358fdEntry_t *fdEntry = getFdEntry(FD); \359if (fdEntry == NULL) { \360errno = EBADF; \361return -1; \362} \363do { \364startOp(fdEntry, &self); \365ret = FUNC; \366endOp(fdEntry, &self); \367} while (ret == -1 && errno == EINTR); \368return ret; \369}370371int NET_Read(int s, void* buf, size_t len) {372BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) );373}374375int NET_NonBlockingRead(int s, void* buf, size_t len) {376BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, MSG_DONTWAIT));377}378379int NET_RecvFrom(int s, void *buf, int len, unsigned int flags,380struct sockaddr *from, socklen_t *fromlen) {381BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) );382}383384int NET_Send(int s, void *msg, int len, unsigned int flags) {385BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) );386}387388int NET_SendTo(int s, const void *msg, int len, unsigned int389flags, const struct sockaddr *to, int tolen) {390BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) );391}392393int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {394BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) );395}396397int NET_Connect(int s, struct sockaddr *addr, int addrlen) {398BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );399}400401int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {402BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );403}404405/*406* Wrapper for select(s, timeout). We are using select() on Mac OS due to Bug 7131399.407* Auto restarts with adjusted timeout if interrupted by408* signal other than our wakeup signal.409*/410int NET_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp) {411struct timeval t, *tp = &t;412fd_set fds;413fd_set* fdsp = NULL;414int allocated = 0;415threadEntry_t self;416fdEntry_t *fdEntry = getFdEntry(s);417418/*419* Check that fd hasn't been closed.420*/421if (fdEntry == NULL) {422errno = EBADF;423return -1;424}425426/*427* Pick up current time as may need to adjust timeout428*/429if (timeout > 0) {430/* Timed */431t.tv_sec = timeout / 1000;432t.tv_usec = (timeout % 1000) * 1000;433} else if (timeout < 0) {434/* Blocking */435tp = 0;436} else {437/* Poll */438t.tv_sec = 0;439t.tv_usec = 0;440}441442if (s < FD_SETSIZE) {443fdsp = &fds;444FD_ZERO(fdsp);445} else {446int length = (howmany(s+1, NFDBITS)) * sizeof(int);447fdsp = (fd_set *) calloc(1, length);448if (fdsp == NULL) {449return -1; // errno will be set to ENOMEM450}451allocated = 1;452}453FD_SET(s, fdsp);454455jlong prevNanoTime = nanoTimeStamp;456jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;457for(;;) {458int rv;459460/*461* call select on the fd. If interrupted by our wakeup signal462* errno will be set to EBADF.463*/464465startOp(fdEntry, &self);466rv = select(s+1, fdsp, 0, 0, tp);467endOp(fdEntry, &self);468469/*470* If interrupted then adjust timeout. If timeout471* has expired return 0 (indicating timeout expired).472*/473if (rv < 0 && errno == EINTR) {474if (timeout > 0) {475jlong newNanoTime = JVM_NanoTime(env, 0);476nanoTimeout -= newNanoTime - prevNanoTime;477if (nanoTimeout < NET_NSEC_PER_MSEC) {478if (allocated != 0)479free(fdsp);480return 0;481}482prevNanoTime = newNanoTime;483t.tv_sec = nanoTimeout / NET_NSEC_PER_SEC;484t.tv_usec = (nanoTimeout % NET_NSEC_PER_SEC) / NET_NSEC_PER_USEC;485} else {486continue; // timeout is -1, so loop again.487}488} else {489if (allocated != 0)490free(fdsp);491return rv;492}493}494}495496497