home *** CD-ROM | disk | FTP | other *** search
- /*
- * @(#)Poller.c 1.9 01/12/03
- *
- * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- /*
- **********************************************************************
- * 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 ommitted here.
- *
- * Compile "cc -G -o <dest_dir>/libpoller.so -I ${JAVA_HOME}/include " \
- * -I ${JAVA_HOME}/include/solaris Poller.c" and place the <dest_dir>
- * in your LD_LIBRARY_PATH
- *
- **********************************************************************
- */
-
- #include <stdio.h>
- #include <unistd.h>
- #include <errno.h>
- #include <poll.h>
- #include <malloc.h>
- #include <fcntl.h>
-
-
- /*
- * 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 <sys/devpoll.h>
- #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;
- }
-
-