/*
*
* 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
*
* - 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
*
* One will need Solaris 8 or Solaris 7 with adequate patches to take
* 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.
* (DP_POLL), but this complicates exception handling and sees
* only modest performance gains so wasn't done.
* 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 " \
* 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.
*/
#define DEBUG_NOT
#define DEVPOLL_NOT
#ifdef DEVPOLL
#endif
#include "Poller.h"
#ifdef DEBUG
#define ASSERT(x) {if (!(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;
} ioevent_t;
/*
* Exceptions to be thrown.
* Note : assuming all illegal argument and NULL pointer checks
* have already been done by the Java calling methods.
*/
{
cause);
return -1;
}
{
cause);
return -1;
}
{
cause);
return -1;
}
"Poller:" str)
/*
* 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.
*/
{
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!
*/
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.
*/
{
Use_devpoll = 0;
}
else
{
Use_devpoll = 1;
}
}
#endif
/*
* 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++) {
}
/*
* this tells me the max number of open filedescriptors
*/
if (Max_index < 0) {
Max_index = 1024;
}
return 0;
}
{
return sysconf(_SC_NPROCESSORS_ONLN);
}
/*
* Class: Poller
* Method: nativeCreatePoller
* Signature: (I)I
* using more than one poll array could hurt
* Solaris 7 performance due to kernel caching.
*/
{
if (maximum_fds == -1) {
}
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 {
}
ioeh->last_index = 0;
ioeh->total_free = 0;
ioeh->left_events = 0;
if (Use_devpoll) {
return MEMORY_EXCEPTION("CreatePoller - can\'t open /dev/poll");
}
}
return MEMORY_EXCEPTION("CreatePoller - malloc failure");
}
return retval;
}
/*
* Class: Poller
* Method: nativeDestroyPoller
* Signature: (I)V
*/
{
{
STATE_EXCEPTION("DestroyPoller - handle out of range");
return;
}
if (Use_devpoll) {
}
}
#ifdef DEBUG
{
for (i = 0; i < ioeh->last_index; i++)
{
unused++;
else
used++;
}
printf("WARNING : found %d free, claimed %d. Used : %d\n",
}
#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()).
*/
{
int retval;
return STATE_EXCEPTION("AddFd - handle out of range");
#ifdef DEVPOLL
if (Use_devpoll)
{
int i;
/*
*/
sizeof(pollfd_t)) {
DBGMSG(("write to devpollfd=%d showed %d bytes out of %d\n",
return STATE_EXCEPTION("AddFd - /dev/poll add failure");
}
else
{
}
}
else
#endif
}
return retval;
}
/*
* Addfd to pollfd array...optimized for Solaris 7
*/
{
int idx;
if (ioeh->total_free)
{
/*
* Traversing from end because that's where we pad.
*/
ioeh->total_free--;
break;
}
}
{
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.
*/
{
}
DBGMSG(("Just grew from %d to %d in size\n",
}
return fd;
}
/*
* Class: Poller
* Method: nativeRemoveFd
* Signature: (II)I
*/
{
return STATE_EXCEPTION("RemoveFd - handle out of range");
#ifdef DEVPOLL
if (Use_devpoll)
{
/*
*/
{
return STATE_EXCEPTION("RemoveFd - /dev/poll failure");
}
}
else
#endif DEVPOLL
{
}
}
/*
* remove from pollfd array...optimize for Solaris 7
*/
{
int i;
int found = 0;
{ /* !Use_devpoll */
for (i = 0; i < ioeh->last_index; i++)
{
{
found = 1;
break;
}
}
if (!found)
{
return STATE_EXCEPTION("RemoveFd - no such fd");
}
ioeh->total_free++;
/*
* Shrinking pool if > 33% empty. Just don't do this often!
*/
{
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.
*/
{
{
j++;
DBGMSG( ("i=%d,j=%d,ioeh->pfd[j].fd=%d\n",
if (j < i)
{
}
}
}
DBGMSG(("Just shrunk from %d to %d in size\n",
}
} /* !Use_devpoll */
return 1;
}
/*
* Class: Poller
* Method: nativeIsMember
* Signature: (II)I
*/
{
int found = 0;
int i;
return STATE_EXCEPTION("IsMember - handle out of range");
#ifdef DEVPOLL
if (Use_devpoll)
{
/*
* DEVPOLL ioctl DP_ISPOLLED call to determine if fd is polled.
*/
if (found == -1)
{
return STATE_EXCEPTION("IsMember - /dev/poll failure");
}
}
else
#endif
{
for (i = 0; i < ioeh->last_index; i++)
{
{
found = 1;
break;
}
}
}
return found;
}
/*
* Class: Poller
* Method: nativeWait
* Signature: (II[I[SJ)I
*/
{
short *reventp;
int retval;
return STATE_EXCEPTION("nativeWait - handle out of range");
if (maxEvents == 0) /* just doing a kernel delay! */
{
return 0;
}
#ifdef DEVPOLL
if (Use_devpoll)
{
/*
* DEVPOLL ioctl DP_POLL call, reading
*/
if (useEvents == -1)
{
return INTERRUPT_EXCEPTION("nativeWait - /dev/poll failure EINTR");
else
return STATE_EXCEPTION("nativeWait - /dev/poll failure");
}
{
{
count++;
}
}
return STATE_EXCEPTION("Wait - Corrupted internals");
}
else
#endif
{ /* !Use_devpoll */
/* no leftovers=>go to kernel */
if (ioeh->left_events == 0)
{
if (useEvents == -1)
{
return INTERRUPT_EXCEPTION("Wait - poll() failure EINTR-" \
"IO interrupted.");
return STATE_EXCEPTION("Wait - poll() failure EINVAL-" \
"invalid args (is fdlim cur < max?)");
else
return STATE_EXCEPTION("Wait - poll() failure");
}
}
else
{ /* left over from last call */
}
{
}
if (useEvents > 0)
{
{
{
/* in case of leftover for next walk */
count++;
}
}
{
ioeh->left_events = 0;
return STATE_EXCEPTION("Wait - Corrupted internals");
}
}
} /* !Use_devpoll */
return useEvents;
}