jdk/src/demo/solaris/jni/Poller/Poller.c
changeset 25859 3317bb8137f4
parent 23895 ede5bb2c36bf
--- /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 <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;
+}