Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/demo/jni/Poller/Poller.c
32287 views
/*1* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031/*32* This source code is provided to illustrate the usage of a given feature33* or technique and has been deliberately simplified. Additional steps34* required for a production-quality application, such as security checks,35* input validation and proper error handling, might not be present in36* this sample code.37*/383940/*41**********************************************************************42* Poller.c :43* JNI code for use with Poller.java, principally to take advantage44* of poll() or /dev/poll multiplexing.45*46* One will need Solaris 8 or Solaris 7 with adequate patches to take47* advantage of the /dev/poll performance enhancements, though any48* version of Solaris 7 will automatically use the kernel poll()49* caching. And poll() will function in 2.5.1 and 2.6 as well, but50* will not perform well for large numbers of file descriptors.51*52* Several assumptions have been made to simplify this code :53* 1> At most MAX_HANDLES (32) separate pollable entities are currently54* supported.55* 2> Global synchronization from Java is assumed for all init, create56* and destroy routines. Per Object (handle passed in) synchronization57* is required for all AddFd, RemoveFd, IsMember, and Wait routines.58* 3> It is currently up to the user to handle waking up an59* existing nativeWait() call to do an addfd or removefd on60* that set...could implement that here with an extra pipe, or61* with a pair of loopback sockets in Poller.java or user code.62* In most cases interruption is not necessary for deletions,63* so long as deletions are queued up outside the Poller class64* and then executed the next time waitMultiple() returns.65* 4> /dev/poll performance could be slightly improved by coalescing66* adds/removes so that a write() is only done before the ioctl67* (DP_POLL), but this complicates exception handling and sees68* only modest performance gains so wasn't done.69* 5> /dev/poll does not report errors on attempts to remove non-70* extant fds, but a future bug fix to the /dev/poll device driver71* should solve this problem.72* 6> Could add simpler code for pre-Solaris 7 releases which will73* perform slightly better on those OSs. But again there74* are only modest gains to be had from these new code paths,75* so they've been omitted here.76*77* Compile "cc -G -o <dest_dir>/libpoller.so -I ${JAVA_HOME}/include " \78* -I ${JAVA_HOME}/include/solaris Poller.c" and place the <dest_dir>79* in your LD_LIBRARY_PATH80*81**********************************************************************82*/8384#include <stdio.h>85#include <unistd.h>86#include <errno.h>87#include <poll.h>88#include <malloc.h>89#include <fcntl.h>909192/*93* Remove "_NOT"s to turn on features94* Append "_NOT" to turn off features.95* Use of /dev/poll requires both the include file and kernel driver.96*/97#define DEBUG_NOT98#define DEVPOLL_NOT99100#ifdef DEVPOLL101#include <sys/devpoll.h>102#endif103104#include "Poller.h"105106#define MAX_HANDLES 32107108109#ifdef DEBUG110#define DBGMSG(x) printf x111#define ASSERT(x) {if (!(x)) \112printf("assertion(%s) failed at line : %d\n",#x,__LINE__);}113#define CHECK_HANDLE(x) check_handle(x)114#else115#define DBGMSG(x)116#define ASSERT(x)117#define CHECK_HANDLE(x)118#endif119120/*121* Globals ...protect all with a global synchronization object.122*/123124static int Current_handle = 0;125static int Use_devpoll = 0;126static int Max_index = 0;127128/*129* Per Poller object data.130* Must be synchronized on a per Poller object basis.131*/132133typedef struct ioevent {134int inuse;135int devpollfd;136int last_index;137int total_free;138int left_events;139int max_index;140pollfd_t *pfd;141} ioevent_t;142143static ioevent_t IOE_handles[MAX_HANDLES];144145/*146* Exceptions to be thrown.147* Note : assuming all illegal argument and NULL pointer checks148* have already been done by the Java calling methods.149*/150static jint throwOutOfMemoryError(JNIEnv *env, const char * cause)151{152(*env)->ThrowNew(env, (*env)->FindClass(env,"java/lang/OutOfMemoryError"),153cause);154return -1;155}156static jint throwInterruptedIOException(JNIEnv *env, const char * cause)157{158(*env)->ThrowNew(env,159(*env)->FindClass(env,"java/io/InterruptedIOException"),160cause);161return -1;162}163static jint throwIllegalStateException(JNIEnv *env, const char * cause)164{165(*env)->ThrowNew(env,166(*env)->FindClass(env,"java/lang/IllegalStateException"),167cause);168return -1;169}170171#define MEMORY_EXCEPTION(str) throwOutOfMemoryError(env, "Poller:" str)172#define STATE_EXCEPTION(str) throwIllegalStateException(env, "Poller:" str)173#define INTERRUPT_EXCEPTION(str) throwInterruptedIOException(env, \174"Poller:" str)175jint addfd(JNIEnv *, ioevent_t *, jint, jshort);176jint removefd(JNIEnv *, ioevent_t *, jint);177178/*179* Class Poller180* Method: nativeInit181* Signature: ()I182*183* Only to be called once, right after this library is loaded,184* so no need to deal with reentrancy here.185* Could do as a pragma ini, but that isn't as portable.186*/187JNIEXPORT jint JNICALL Java_Poller_nativeInit(JNIEnv *env, jclass cls)188{189int testdevpollfd;190int i;191192#ifdef DEVPOLL193/*194* See if we can use this much faster method195* Note : must have fix for BUGID # 4223353 or OS can crash!196*/197testdevpollfd = open("/dev/poll",O_RDWR);198if (testdevpollfd >= 0) {199/*200* If Solaris 7, we need a patch201* Until we know what string to search for, we'll play it202* safe and disable this for Solaris 7.203*/204205if (!strcmp(name.release,"5.7"))206{207Use_devpoll = 0;208}209else210{211Use_devpoll = 1;212}213}214215DBGMSG(("Use_devpoll=%d\n" ,Use_devpoll));216close(testdevpollfd);217#endif218219/*220* For now, we optimize for Solaris 7 if /dev/poll isn't221* available, as it is only a small % hit for Solaris < 7.222* if ( (Use_devpoll == 0) && !strcmp(name.release,"5.6") )223* Use_sol7opt = 0;224*/225Current_handle = 0;226for (i = 0; i < MAX_HANDLES; i++) {227IOE_handles[i].devpollfd = -1;228IOE_handles[i].pfd = NULL;229}230231/*232* this tells me the max number of open filedescriptors233*/234Max_index = sysconf(_SC_OPEN_MAX);235if (Max_index < 0) {236Max_index = 1024;237}238239DBGMSG(("got sysconf(_SC_OPEN_MAX)=%d file desc\n",Max_index));240241return 0;242}243244JNIEXPORT jint JNICALL Java_Poller_getNumCPUs(JNIEnv *env, jclass cls)245{246return sysconf(_SC_NPROCESSORS_ONLN);247}248249/*250* Class: Poller251* Method: nativeCreatePoller252* Signature: (I)I253* Note : in the case where /dev/poll doesn't exist,254* using more than one poll array could hurt255* Solaris 7 performance due to kernel caching.256*/257258JNIEXPORT jint JNICALL Java_Poller_nativeCreatePoller259(JNIEnv *env, jobject obj, jint maximum_fds)260{261int handle, retval, i;262ioevent_t *ioeh;263264if (maximum_fds == -1) {265maximum_fds = Max_index;266}267handle = Current_handle;268if (Current_handle >= MAX_HANDLES) {269for (i = 0; i < MAX_HANDLES; i++) {270if (IOE_handles[i].inuse == 0) {271handle = i;272break;273}274}275if (handle >= MAX_HANDLES) {276return MEMORY_EXCEPTION("CreatePoller - MAX_HANDLES exceeded");277}278} else {279Current_handle++;280}281282ioeh = &IOE_handles[handle];283284ioeh->inuse = 1;285286ioeh->last_index = 0;287ioeh->total_free = 0;288ioeh->left_events = 0;289ioeh->max_index = maximum_fds;290291retval = handle;292if (Use_devpoll) {293ioeh->devpollfd = open("/dev/poll",O_RDWR);294DBGMSG(("Opened /dev/poll, set devpollfd = %d\n",ioeh->devpollfd));295if (ioeh->devpollfd < 0) {296Current_handle--;297return MEMORY_EXCEPTION("CreatePoller - can\'t open /dev/poll");298}299}300ioeh->pfd = malloc(maximum_fds * sizeof(pollfd_t));301if (ioeh->pfd == NULL) {302Current_handle--;303return MEMORY_EXCEPTION("CreatePoller - malloc failure");304}305306return retval;307}308309/*310* Class: Poller311* Method: nativeDestroyPoller312* Signature: (I)V313*/314JNIEXPORT void JNICALL Java_Poller_nativeDestroyPoller315(JNIEnv *env, jobject obj, jint handle)316{317318ioevent_t *ioeh;319320if (handle < 0 || handle >= MAX_HANDLES)321{322STATE_EXCEPTION("DestroyPoller - handle out of range");323return;324}325326ioeh = &IOE_handles[handle];327ioeh->inuse = 0;328if (Use_devpoll) {329close(ioeh->devpollfd);330}331free(ioeh->pfd);332}333334#ifdef DEBUG335static void check_handle(ioevent_t *ioeh)336{337int i,used,unused;338339used=unused=0;340for (i = 0; i < ioeh->last_index; i++)341{342if (ioeh->pfd[i].fd == -1)343unused++;344else345used++;346}347if (unused != ioeh->total_free)348printf("WARNING : found %d free, claimed %d. Used : %d\n",349unused, ioeh->total_free, used);350}351#endif352353/*354* Class: Poller355* Method: nativeAddFd356* Signature: (IIS)I357*358* Currently doesn't check to make sure we aren't adding359* an fd already added (no problem for /dev/poll...just360* an array waster for poll()).361*/362JNIEXPORT jint JNICALL Java_Poller_nativeAddFd363(JNIEnv *env, jobject obj, jint handle, jint fd, jshort events)364{365int retval;366ioevent_t *ioeh;367368if (handle < 0 || handle >= MAX_HANDLES)369return STATE_EXCEPTION("AddFd - handle out of range");370371ioeh = &IOE_handles[handle];372373CHECK_HANDLE(ioeh);374375#ifdef DEVPOLL376if (Use_devpoll)377{378int i;379pollfd_t pollelt;380381/*382* use /dev/poll383*/384pollelt.fd = fd;385pollelt.events = events;386if ((i = write(ioeh->devpollfd, &pollelt, sizeof(pollfd_t))) !=387sizeof(pollfd_t)) {388DBGMSG(("write to devpollfd=%d showed %d bytes out of %d\n",389ioeh->devpollfd,i,sizeof(pollfd_t)));390return STATE_EXCEPTION("AddFd - /dev/poll add failure");391}392else393{394retval = fd;395}396}397else398#endif399{ /* no /dev/poll available */400retval = addfd(env, ioeh, fd, events);401}402return retval;403}404405/*406* Addfd to pollfd array...optimized for Solaris 7407*/408jint addfd(JNIEnv *env, ioevent_t *ioeh, jint fd, jshort events)409{410int idx;411412if (ioeh->total_free)413{414/*415* Traversing from end because that's where we pad.416*/417ioeh->total_free--;418for (idx = ioeh->last_index - 1; idx >= 0; idx--) {419if (ioeh->pfd[idx].fd == -1)420break;421}422}423else if (ioeh->last_index >= ioeh->max_index)424{425return MEMORY_EXCEPTION("AddFd - too many fds");426}427else428{429int i;430int new_total;431/*432* For Solaris 7, want to add some growth space433* and fill extras with fd=-1. This allows for434* kernel poll() implementation to perform optimally.435*/436new_total = ioeh->last_index;437new_total += (new_total/10) + 1; /* bump size by 10% */438if (new_total > ioeh->max_index)439new_total = ioeh->max_index;440for (i = ioeh->last_index; i <= new_total; i++)441{442ioeh->pfd[i].fd = -1;443}444idx = ioeh->last_index;445ioeh->total_free = new_total - ioeh->last_index - 1;446DBGMSG(("Just grew from %d to %d in size\n",447ioeh->last_index, new_total));448ioeh->last_index = new_total;449}450ASSERT((idx >= 0) && (idx <= ioeh->max_index));451ASSERT(ioeh->pfd[idx].fd == -1);452ioeh->pfd[idx].fd = fd;453ioeh->pfd[idx].events = events;454ioeh->pfd[idx].revents = 0;455456CHECK_HANDLE(ioeh);457458return fd;459}460461/*462* Class: Poller463* Method: nativeRemoveFd464* Signature: (II)I465*/466JNIEXPORT jint JNICALL Java_Poller_nativeRemoveFd467(JNIEnv *env, jobject obj, jint handle, jint fd)468{469ioevent_t *ioeh;470471if (handle < 0 || handle >= MAX_HANDLES)472return STATE_EXCEPTION("RemoveFd - handle out of range");473474ioeh = &IOE_handles[handle];475476#ifdef DEVPOLL477if (Use_devpoll)478{479/*480* use /dev/poll - currently no need for locking here.481*/482pollfd_t pollelt;483484pollelt.fd = fd;485pollelt.events = POLLREMOVE;486if (write(ioeh->devpollfd, &pollelt,487sizeof(pollfd_t) ) != sizeof(pollfd_t))488{489return STATE_EXCEPTION("RemoveFd - /dev/poll failure");490}491}492else493#endif DEVPOLL494{495return removefd(env, ioeh,fd);496}497}498/*499* remove from pollfd array...optimize for Solaris 7500*/501jint removefd(JNIEnv *env, ioevent_t *ioeh, jint fd)502{503int i;504int found = 0;505506{ /* !Use_devpoll */507for (i = 0; i < ioeh->last_index; i++)508{509if (ioeh->pfd[i].fd == fd)510{511ioeh->pfd[i].fd = -1;512found = 1;513break;514}515}516if (!found)517{518return STATE_EXCEPTION("RemoveFd - no such fd");519}520ioeh->left_events = 0; /* Have to go back to the kernel */521ioeh->total_free++;522/*523* Shrinking pool if > 33% empty. Just don't do this often!524*/525if ( (ioeh->last_index > 100) &&526(ioeh->total_free > (ioeh->last_index / 3)) )527{528int j;529/*530* we'll just bite the bullet here, since we're > 33% empty.531* walk through and eliminate -1 fd values, shrink total532* size to still have ~ 10 fd==-1 values at end.533* Start at end (since we pad here) and, when we find fd != -1,534* swap with an earlier fd == -1 until we have all -1 values535* at the end.536*/537CHECK_HANDLE(ioeh);538for (i = ioeh->last_index - 1, j = 0; i > j; i--)539{540if (ioeh->pfd[i].fd != -1)541{542while ( (j < i) && (ioeh->pfd[j].fd != -1) )543j++;544DBGMSG( ("i=%d,j=%d,ioeh->pfd[j].fd=%d\n",545i, j, ioeh->pfd[j].fd) );546if (j < i)547{548ASSERT(ioeh->pfd[j].fd == -1);549ioeh->pfd[j].fd = ioeh->pfd[i].fd;550ioeh->pfd[j].events = ioeh->pfd[i].events;551ioeh->pfd[i].fd = -1;552}553}554}555DBGMSG(("Just shrunk from %d to %d in size\n",556ioeh->last_index, j+11));557ioeh->last_index = j + 11; /* last_index always 1 greater */558ioeh->total_free = 10;559CHECK_HANDLE(ioeh);560}561} /* !Use_devpoll */562563return 1;564}565566/*567* Class: Poller568* Method: nativeIsMember569* Signature: (II)I570*/571JNIEXPORT jint JNICALL Java_Poller_nativeIsMember572(JNIEnv *env, jobject obj, jint handle, jint fd)573{574int found = 0;575int i;576ioevent_t *ioeh;577578if (handle < 0 || handle >= MAX_HANDLES)579return STATE_EXCEPTION("IsMember - handle out of range");580581ioeh = &IOE_handles[handle];582583#ifdef DEVPOLL584if (Use_devpoll)585{586pollfd_t pfd;587/*588* DEVPOLL ioctl DP_ISPOLLED call to determine if fd is polled.589*/590pfd.fd = fd;591pfd.events = 0;592pfd.revents = 0;593found = ioctl(ioeh->devpollfd, DP_ISPOLLED, &pfd);594if (found == -1)595{596return STATE_EXCEPTION("IsMember - /dev/poll failure");597}598}599else600#endif601{602for (i = 0; i < ioeh->last_index; i++)603{604if (fd == ioeh->pfd[i].fd)605{606found = 1;607break;608}609}610}611612return found;613}614615/*616* Class: Poller617* Method: nativeWait618* Signature: (II[I[SJ)I619*/620JNIEXPORT jint JNICALL Java_Poller_nativeWait621(JNIEnv *env, jobject obj, jint handle, jint maxEvents,622jintArray jfds, jshortArray jrevents, jlong timeout)623{624int useEvents, count, idx;625short *reventp;626jint *fdp;627int retval;628ioevent_t *ioeh;629jboolean isCopy1,isCopy2;630631if (handle < 0 || handle >= MAX_HANDLES)632return STATE_EXCEPTION("nativeWait - handle out of range");633634ioeh = &IOE_handles[handle];635636if (maxEvents == 0) /* just doing a kernel delay! */637{638useEvents = poll(NULL,0L,timeout);639return 0;640}641642#ifdef DEVPOLL643if (Use_devpoll)644{645struct dvpoll dopoll;646/*647* DEVPOLL ioctl DP_POLL call, reading648*/649dopoll.dp_timeout = timeout;650dopoll.dp_nfds=maxEvents;651dopoll.dp_fds=ioeh->pfd;652653useEvents = ioctl(ioeh->devpollfd, DP_POLL, &dopoll);654while ((useEvents == -1) && (errno == EAGAIN))655useEvents = ioctl(ioeh->devpollfd, DP_POLL, &dopoll);656657if (useEvents == -1)658{659if (errno == EINTR)660return INTERRUPT_EXCEPTION("nativeWait - /dev/poll failure EINTR");661else662return STATE_EXCEPTION("nativeWait - /dev/poll failure");663}664665reventp =(*env)->GetShortArrayElements(env,jrevents,&isCopy1);666fdp =(*env)->GetIntArrayElements(env,jfds,&isCopy2);667for (idx = 0,count = 0; idx < useEvents; idx++)668{669if (ioeh->pfd[idx].revents)670{671fdp[count] = ioeh->pfd[idx].fd;672reventp[count] = ioeh->pfd[idx].revents;673count++;674}675}676if (count < useEvents)677return STATE_EXCEPTION("Wait - Corrupted internals");678679if (isCopy1 == JNI_TRUE)680(*env)->ReleaseShortArrayElements(env,jrevents,reventp,0);681if (isCopy2 == JNI_TRUE)682(*env)->ReleaseIntArrayElements(env,jfds,fdp,0);683}684else685#endif686{ /* !Use_devpoll */687688/* no leftovers=>go to kernel */689if (ioeh->left_events == 0)690{691useEvents = poll(ioeh->pfd,ioeh->last_index, timeout);692while ((useEvents == -1) && (errno == EAGAIN))693useEvents = poll(ioeh->pfd,ioeh->last_index, timeout);694if (useEvents == -1)695{696if (errno == EINTR)697return INTERRUPT_EXCEPTION("Wait - poll() failure EINTR-" \698"IO interrupted.");699else if (errno == EINVAL)700return STATE_EXCEPTION("Wait - poll() failure EINVAL-" \701"invalid args (is fdlim cur < max?)");702else703return STATE_EXCEPTION("Wait - poll() failure");704}705ioeh->left_events = useEvents;706DBGMSG(("waitnative : poll returns : %d\n",useEvents));707}708else709{ /* left over from last call */710useEvents = ioeh->left_events;711}712713if (useEvents > maxEvents)714{715useEvents = maxEvents;716}717718ioeh->left_events -= useEvents; /* left to process */719720DBGMSG(("waitnative : left %d, use %d, max %d\n",ioeh->left_events,721useEvents,maxEvents));722723if (useEvents > 0)724{725reventp =(*env)->GetShortArrayElements(env,jrevents,&isCopy1);726fdp =(*env)->GetIntArrayElements(env,jfds,&isCopy2);727for (idx = 0,count = 0; (idx < ioeh->last_index) &&728(count < useEvents); idx++)729{730if (ioeh->pfd[idx].revents)731{732fdp[count] = ioeh->pfd[idx].fd;733reventp[count] = ioeh->pfd[idx].revents;734/* in case of leftover for next walk */735ioeh->pfd[idx].revents = 0;736count++;737}738}739if (count < useEvents)740{741ioeh->left_events = 0;742return STATE_EXCEPTION("Wait - Corrupted internals");743}744if (isCopy1 == JNI_TRUE)745(*env)->ReleaseShortArrayElements(env,jrevents,reventp,0);746if (isCopy2 == JNI_TRUE)747(*env)->ReleaseIntArrayElements(env,jfds,fdp,0);748}749} /* !Use_devpoll */750751return useEvents;752}753754755