diff -r 836adbf7a2cd -r 3317bb8137f4 jdk/src/demo/solaris/jni/Poller/Poller.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/demo/solaris/jni/Poller/Poller.c Sun Aug 17 15:54:13 2014 +0100 @@ -0,0 +1,753 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This source code is provided to illustrate the usage of a given feature + * or technique and has been deliberately simplified. Additional steps + * required for a production-quality application, such as security checks, + * input validation and proper error handling, might not be present in + * this sample code. + */ + + +/* + ********************************************************************** + * Poller.c : + * JNI code for use with Poller.java, principally to take advantage + * of poll() or /dev/poll multiplexing. + * + * One will need Solaris 8 or Solaris 7 with adequate patches to take + * advantage of the /dev/poll performance enhancements, though any + * version of Solaris 7 will automatically use the kernel poll() + * caching. And poll() will function in 2.5.1 and 2.6 as well, but + * will not perform well for large numbers of file descriptors. + * + * Several assumptions have been made to simplify this code : + * 1> At most MAX_HANDLES (32) separate pollable entities are currently + * supported. + * 2> Global synchronization from Java is assumed for all init, create + * and destroy routines. Per Object (handle passed in) synchronization + * is required for all AddFd, RemoveFd, IsMember, and Wait routines. + * 3> It is currently up to the user to handle waking up an + * existing nativeWait() call to do an addfd or removefd on + * that set...could implement that here with an extra pipe, or + * with a pair of loopback sockets in Poller.java or user code. + * In most cases interruption is not necessary for deletions, + * so long as deletions are queued up outside the Poller class + * and then executed the next time waitMultiple() returns. + * 4> /dev/poll performance could be slightly improved by coalescing + * adds/removes so that a write() is only done before the ioctl + * (DP_POLL), but this complicates exception handling and sees + * only modest performance gains so wasn't done. + * 5> /dev/poll does not report errors on attempts to remove non- + * extant fds, but a future bug fix to the /dev/poll device driver + * should solve this problem. + * 6> Could add simpler code for pre-Solaris 7 releases which will + * perform slightly better on those OSs. But again there + * are only modest gains to be had from these new code paths, + * so they've been omitted here. + * + * Compile "cc -G -o /libpoller.so -I ${JAVA_HOME}/include " \ + * -I ${JAVA_HOME}/include/solaris Poller.c" and place the + * in your LD_LIBRARY_PATH + * + ********************************************************************** + */ + +#include +#include +#include +#include +#include +#include + + +/* + * Remove "_NOT"s to turn on features + * Append "_NOT" to turn off features. + * Use of /dev/poll requires both the include file and kernel driver. + */ +#define DEBUG_NOT +#define DEVPOLL_NOT + +#ifdef DEVPOLL +#include +#endif + +#include "Poller.h" + +#define MAX_HANDLES 32 + + +#ifdef DEBUG +#define DBGMSG(x) printf x +#define ASSERT(x) {if (!(x)) \ + printf("assertion(%s) failed at line : %d\n",#x,__LINE__);} +#define CHECK_HANDLE(x) check_handle(x) +#else +#define DBGMSG(x) +#define ASSERT(x) +#define CHECK_HANDLE(x) +#endif + +/* + * Globals ...protect all with a global synchronization object. + */ + +static int Current_handle = 0; +static int Use_devpoll = 0; +static int Max_index = 0; + +/* + * Per Poller object data. + * Must be synchronized on a per Poller object basis. + */ + +typedef struct ioevent { + int inuse; + int devpollfd; + int last_index; + int total_free; + int left_events; + int max_index; + pollfd_t *pfd; +} ioevent_t; + +static ioevent_t IOE_handles[MAX_HANDLES]; + + /* + * Exceptions to be thrown. + * Note : assuming all illegal argument and NULL pointer checks + * have already been done by the Java calling methods. + */ +static jint throwOutOfMemoryError(JNIEnv *env, const char * cause) +{ + (*env)->ThrowNew(env, (*env)->FindClass(env,"java/lang/OutOfMemoryError"), + cause); + return -1; +} +static jint throwInterruptedIOException(JNIEnv *env, const char * cause) +{ + (*env)->ThrowNew(env, + (*env)->FindClass(env,"java/io/InterruptedIOException"), + cause); + return -1; +} +static jint throwIllegalStateException(JNIEnv *env, const char * cause) +{ + (*env)->ThrowNew(env, + (*env)->FindClass(env,"java/lang/IllegalStateException"), + cause); + return -1; +} + +#define MEMORY_EXCEPTION(str) throwOutOfMemoryError(env, "Poller:" str) +#define STATE_EXCEPTION(str) throwIllegalStateException(env, "Poller:" str) +#define INTERRUPT_EXCEPTION(str) throwInterruptedIOException(env, \ + "Poller:" str) +jint addfd(JNIEnv *, ioevent_t *, jint, jshort); +jint removefd(JNIEnv *, ioevent_t *, jint); + + /* + * Class Poller + * Method: nativeInit + * Signature: ()I + * + * Only to be called once, right after this library is loaded, + * so no need to deal with reentrancy here. + * Could do as a pragma ini, but that isn't as portable. + */ +JNIEXPORT jint JNICALL Java_Poller_nativeInit(JNIEnv *env, jclass cls) +{ + int testdevpollfd; + int i; + +#ifdef DEVPOLL + /* + * See if we can use this much faster method + * Note : must have fix for BUGID # 4223353 or OS can crash! + */ + testdevpollfd = open("/dev/poll",O_RDWR); + if (testdevpollfd >= 0) { + /* + * If Solaris 7, we need a patch + * Until we know what string to search for, we'll play it + * safe and disable this for Solaris 7. + */ + + if (!strcmp(name.release,"5.7")) + { + Use_devpoll = 0; + } + else + { + Use_devpoll = 1; + } + } + + DBGMSG(("Use_devpoll=%d\n" ,Use_devpoll)); + close(testdevpollfd); +#endif + + /* + * For now, we optimize for Solaris 7 if /dev/poll isn't + * available, as it is only a small % hit for Solaris < 7. + * if ( (Use_devpoll == 0) && !strcmp(name.release,"5.6") ) + * Use_sol7opt = 0; + */ + Current_handle = 0; + for (i = 0; i < MAX_HANDLES; i++) { + IOE_handles[i].devpollfd = -1; + IOE_handles[i].pfd = NULL; + } + + /* + * this tells me the max number of open filedescriptors + */ + Max_index = sysconf(_SC_OPEN_MAX); + if (Max_index < 0) { + Max_index = 1024; + } + + DBGMSG(("got sysconf(_SC_OPEN_MAX)=%d file desc\n",Max_index)); + + return 0; +} + +JNIEXPORT jint JNICALL Java_Poller_getNumCPUs(JNIEnv *env, jclass cls) +{ + return sysconf(_SC_NPROCESSORS_ONLN); +} + + /* + * Class: Poller + * Method: nativeCreatePoller + * Signature: (I)I + * Note : in the case where /dev/poll doesn't exist, + * using more than one poll array could hurt + * Solaris 7 performance due to kernel caching. + */ + +JNIEXPORT jint JNICALL Java_Poller_nativeCreatePoller + (JNIEnv *env, jobject obj, jint maximum_fds) +{ + int handle, retval, i; + ioevent_t *ioeh; + + if (maximum_fds == -1) { + maximum_fds = Max_index; + } + handle = Current_handle; + if (Current_handle >= MAX_HANDLES) { + for (i = 0; i < MAX_HANDLES; i++) { + if (IOE_handles[i].inuse == 0) { + handle = i; + break; + } + } + if (handle >= MAX_HANDLES) { + return MEMORY_EXCEPTION("CreatePoller - MAX_HANDLES exceeded"); + } + } else { + Current_handle++; + } + + ioeh = &IOE_handles[handle]; + + ioeh->inuse = 1; + + ioeh->last_index = 0; + ioeh->total_free = 0; + ioeh->left_events = 0; + ioeh->max_index = maximum_fds; + + retval = handle; + if (Use_devpoll) { + ioeh->devpollfd = open("/dev/poll",O_RDWR); + DBGMSG(("Opened /dev/poll, set devpollfd = %d\n",ioeh->devpollfd)); + if (ioeh->devpollfd < 0) { + Current_handle--; + return MEMORY_EXCEPTION("CreatePoller - can\'t open /dev/poll"); + } + } + ioeh->pfd = malloc(maximum_fds * sizeof(pollfd_t)); + if (ioeh->pfd == NULL) { + Current_handle--; + return MEMORY_EXCEPTION("CreatePoller - malloc failure"); + } + + return retval; +} + + /* + * Class: Poller + * Method: nativeDestroyPoller + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_Poller_nativeDestroyPoller + (JNIEnv *env, jobject obj, jint handle) +{ + + ioevent_t *ioeh; + + if (handle < 0 || handle >= MAX_HANDLES) + { + STATE_EXCEPTION("DestroyPoller - handle out of range"); + return; + } + + ioeh = &IOE_handles[handle]; + ioeh->inuse = 0; + if (Use_devpoll) { + close(ioeh->devpollfd); + } + free(ioeh->pfd); +} + +#ifdef DEBUG +static void check_handle(ioevent_t *ioeh) +{ + int i,used,unused; + + used=unused=0; + for (i = 0; i < ioeh->last_index; i++) + { + if (ioeh->pfd[i].fd == -1) + unused++; + else + used++; + } + if (unused != ioeh->total_free) + printf("WARNING : found %d free, claimed %d. Used : %d\n", + unused, ioeh->total_free, used); +} +#endif + + /* + * Class: Poller + * Method: nativeAddFd + * Signature: (IIS)I + * + * Currently doesn't check to make sure we aren't adding + * an fd already added (no problem for /dev/poll...just + * an array waster for poll()). + */ +JNIEXPORT jint JNICALL Java_Poller_nativeAddFd + (JNIEnv *env, jobject obj, jint handle, jint fd, jshort events) +{ + int retval; + ioevent_t *ioeh; + + if (handle < 0 || handle >= MAX_HANDLES) + return STATE_EXCEPTION("AddFd - handle out of range"); + + ioeh = &IOE_handles[handle]; + + CHECK_HANDLE(ioeh); + + #ifdef DEVPOLL + if (Use_devpoll) + { + int i; + pollfd_t pollelt; + + /* + * use /dev/poll + */ + pollelt.fd = fd; + pollelt.events = events; + if ((i = write(ioeh->devpollfd, &pollelt, sizeof(pollfd_t))) != + sizeof(pollfd_t)) { + DBGMSG(("write to devpollfd=%d showed %d bytes out of %d\n", + ioeh->devpollfd,i,sizeof(pollfd_t))); + return STATE_EXCEPTION("AddFd - /dev/poll add failure"); + } + else + { + retval = fd; + } + } + else + #endif + { /* no /dev/poll available */ + retval = addfd(env, ioeh, fd, events); + } + return retval; +} + +/* + * Addfd to pollfd array...optimized for Solaris 7 + */ +jint addfd(JNIEnv *env, ioevent_t *ioeh, jint fd, jshort events) +{ + int idx; + + if (ioeh->total_free) + { + /* + * Traversing from end because that's where we pad. + */ + ioeh->total_free--; + for (idx = ioeh->last_index - 1; idx >= 0; idx--) { + if (ioeh->pfd[idx].fd == -1) + break; + } + } + else if (ioeh->last_index >= ioeh->max_index) + { + return MEMORY_EXCEPTION("AddFd - too many fds"); + } + else + { + int i; + int new_total; + /* + * For Solaris 7, want to add some growth space + * and fill extras with fd=-1. This allows for + * kernel poll() implementation to perform optimally. + */ + new_total = ioeh->last_index; + new_total += (new_total/10) + 1; /* bump size by 10% */ + if (new_total > ioeh->max_index) + new_total = ioeh->max_index; + for (i = ioeh->last_index; i <= new_total; i++) + { + ioeh->pfd[i].fd = -1; + } + idx = ioeh->last_index; + ioeh->total_free = new_total - ioeh->last_index - 1; + DBGMSG(("Just grew from %d to %d in size\n", + ioeh->last_index, new_total)); + ioeh->last_index = new_total; + } + ASSERT((idx >= 0) && (idx <= ioeh->max_index)); + ASSERT(ioeh->pfd[idx].fd == -1); + ioeh->pfd[idx].fd = fd; + ioeh->pfd[idx].events = events; + ioeh->pfd[idx].revents = 0; + + CHECK_HANDLE(ioeh); + + return fd; +} + +/* + * Class: Poller + * Method: nativeRemoveFd + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_Poller_nativeRemoveFd + (JNIEnv *env, jobject obj, jint handle, jint fd) +{ + ioevent_t *ioeh; + + if (handle < 0 || handle >= MAX_HANDLES) + return STATE_EXCEPTION("RemoveFd - handle out of range"); + + ioeh = &IOE_handles[handle]; + + #ifdef DEVPOLL + if (Use_devpoll) + { + /* + * use /dev/poll - currently no need for locking here. + */ + pollfd_t pollelt; + + pollelt.fd = fd; + pollelt.events = POLLREMOVE; + if (write(ioeh->devpollfd, &pollelt, + sizeof(pollfd_t) ) != sizeof(pollfd_t)) + { + return STATE_EXCEPTION("RemoveFd - /dev/poll failure"); + } + } + else + #endif DEVPOLL + { + return removefd(env, ioeh,fd); + } +} +/* + * remove from pollfd array...optimize for Solaris 7 + */ +jint removefd(JNIEnv *env, ioevent_t *ioeh, jint fd) +{ + int i; + int found = 0; + + { /* !Use_devpoll */ + for (i = 0; i < ioeh->last_index; i++) + { + if (ioeh->pfd[i].fd == fd) + { + ioeh->pfd[i].fd = -1; + found = 1; + break; + } + } + if (!found) + { + return STATE_EXCEPTION("RemoveFd - no such fd"); + } + ioeh->left_events = 0; /* Have to go back to the kernel */ + ioeh->total_free++; + /* + * Shrinking pool if > 33% empty. Just don't do this often! + */ + if ( (ioeh->last_index > 100) && + (ioeh->total_free > (ioeh->last_index / 3)) ) + { + int j; + /* + * we'll just bite the bullet here, since we're > 33% empty. + * walk through and eliminate -1 fd values, shrink total + * size to still have ~ 10 fd==-1 values at end. + * Start at end (since we pad here) and, when we find fd != -1, + * swap with an earlier fd == -1 until we have all -1 values + * at the end. + */ + CHECK_HANDLE(ioeh); + for (i = ioeh->last_index - 1, j = 0; i > j; i--) + { + if (ioeh->pfd[i].fd != -1) + { + while ( (j < i) && (ioeh->pfd[j].fd != -1) ) + j++; + DBGMSG( ("i=%d,j=%d,ioeh->pfd[j].fd=%d\n", + i, j, ioeh->pfd[j].fd) ); + if (j < i) + { + ASSERT(ioeh->pfd[j].fd == -1); + ioeh->pfd[j].fd = ioeh->pfd[i].fd; + ioeh->pfd[j].events = ioeh->pfd[i].events; + ioeh->pfd[i].fd = -1; + } + } + } + DBGMSG(("Just shrunk from %d to %d in size\n", + ioeh->last_index, j+11)); + ioeh->last_index = j + 11; /* last_index always 1 greater */ + ioeh->total_free = 10; + CHECK_HANDLE(ioeh); + } + } /* !Use_devpoll */ + + return 1; +} + + /* + * Class: Poller + * Method: nativeIsMember + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_Poller_nativeIsMember + (JNIEnv *env, jobject obj, jint handle, jint fd) +{ + int found = 0; + int i; + ioevent_t *ioeh; + + if (handle < 0 || handle >= MAX_HANDLES) + return STATE_EXCEPTION("IsMember - handle out of range"); + + ioeh = &IOE_handles[handle]; + + #ifdef DEVPOLL + if (Use_devpoll) + { + pollfd_t pfd; + /* + * DEVPOLL ioctl DP_ISPOLLED call to determine if fd is polled. + */ + pfd.fd = fd; + pfd.events = 0; + pfd.revents = 0; + found = ioctl(ioeh->devpollfd, DP_ISPOLLED, &pfd); + if (found == -1) + { + return STATE_EXCEPTION("IsMember - /dev/poll failure"); + } + } + else + #endif + { + for (i = 0; i < ioeh->last_index; i++) + { + if (fd == ioeh->pfd[i].fd) + { + found = 1; + break; + } + } + } + + return found; +} + + /* + * Class: Poller + * Method: nativeWait + * Signature: (II[I[SJ)I + */ +JNIEXPORT jint JNICALL Java_Poller_nativeWait + (JNIEnv *env, jobject obj, jint handle, jint maxEvents, + jintArray jfds, jshortArray jrevents, jlong timeout) +{ + int useEvents, count, idx; + short *reventp; + jint *fdp; + int retval; + ioevent_t *ioeh; + jboolean isCopy1,isCopy2; + + if (handle < 0 || handle >= MAX_HANDLES) + return STATE_EXCEPTION("nativeWait - handle out of range"); + + ioeh = &IOE_handles[handle]; + + if (maxEvents == 0) /* just doing a kernel delay! */ + { + useEvents = poll(NULL,0L,timeout); + return 0; + } + + #ifdef DEVPOLL + if (Use_devpoll) + { + struct dvpoll dopoll; + /* + * DEVPOLL ioctl DP_POLL call, reading + */ + dopoll.dp_timeout = timeout; + dopoll.dp_nfds=maxEvents; + dopoll.dp_fds=ioeh->pfd; + + useEvents = ioctl(ioeh->devpollfd, DP_POLL, &dopoll); + while ((useEvents == -1) && (errno == EAGAIN)) + useEvents = ioctl(ioeh->devpollfd, DP_POLL, &dopoll); + + if (useEvents == -1) + { + if (errno == EINTR) + return INTERRUPT_EXCEPTION("nativeWait - /dev/poll failure EINTR"); + else + return STATE_EXCEPTION("nativeWait - /dev/poll failure"); + } + + reventp =(*env)->GetShortArrayElements(env,jrevents,&isCopy1); + fdp =(*env)->GetIntArrayElements(env,jfds,&isCopy2); + for (idx = 0,count = 0; idx < useEvents; idx++) + { + if (ioeh->pfd[idx].revents) + { + fdp[count] = ioeh->pfd[idx].fd; + reventp[count] = ioeh->pfd[idx].revents; + count++; + } + } + if (count < useEvents) + return STATE_EXCEPTION("Wait - Corrupted internals"); + + if (isCopy1 == JNI_TRUE) + (*env)->ReleaseShortArrayElements(env,jrevents,reventp,0); + if (isCopy2 == JNI_TRUE) + (*env)->ReleaseIntArrayElements(env,jfds,fdp,0); + } + else + #endif + { /* !Use_devpoll */ + + /* no leftovers=>go to kernel */ + if (ioeh->left_events == 0) + { + useEvents = poll(ioeh->pfd,ioeh->last_index, timeout); + while ((useEvents == -1) && (errno == EAGAIN)) + useEvents = poll(ioeh->pfd,ioeh->last_index, timeout); + if (useEvents == -1) + { + if (errno == EINTR) + return INTERRUPT_EXCEPTION("Wait - poll() failure EINTR-" \ + "IO interrupted."); + else if (errno == EINVAL) + return STATE_EXCEPTION("Wait - poll() failure EINVAL-" \ + "invalid args (is fdlim cur < max?)"); + else + return STATE_EXCEPTION("Wait - poll() failure"); + } + ioeh->left_events = useEvents; + DBGMSG(("waitnative : poll returns : %d\n",useEvents)); + } + else + { /* left over from last call */ + useEvents = ioeh->left_events; + } + + if (useEvents > maxEvents) + { + useEvents = maxEvents; + } + + ioeh->left_events -= useEvents; /* left to process */ + + DBGMSG(("waitnative : left %d, use %d, max %d\n",ioeh->left_events, + useEvents,maxEvents)); + + if (useEvents > 0) + { + reventp =(*env)->GetShortArrayElements(env,jrevents,&isCopy1); + fdp =(*env)->GetIntArrayElements(env,jfds,&isCopy2); + for (idx = 0,count = 0; (idx < ioeh->last_index) && + (count < useEvents); idx++) + { + if (ioeh->pfd[idx].revents) + { + fdp[count] = ioeh->pfd[idx].fd; + reventp[count] = ioeh->pfd[idx].revents; + /* in case of leftover for next walk */ + ioeh->pfd[idx].revents = 0; + count++; + } + } + if (count < useEvents) + { + ioeh->left_events = 0; + return STATE_EXCEPTION("Wait - Corrupted internals"); + } + if (isCopy1 == JNI_TRUE) + (*env)->ReleaseShortArrayElements(env,jrevents,reventp,0); + if (isCopy2 == JNI_TRUE) + (*env)->ReleaseIntArrayElements(env,jfds,fdp,0); + } + } /* !Use_devpoll */ + + return useEvents; +}