/*******************************************************************************
* BPoll.cpp File poll class
* T.Barnaby, BEAM Ltd, 1/4/05
* updated by D.Korchagin, CERN AB-BI-SW, 2007-08-31
*******************************************************************************
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <BPoll.h>
#ifndef __Lynx__
#else
#include <string.h>
/* Few defines for LynxOS portability NMN */
#define howmany(x,y) (((x)+((y)-1))/(y))
#define nfds_t unsigned long int
#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
static int max_fd_size;
struct timeval tv;
fd_set *rset, *wset, *xset;
struct pollfd *f;
int ready;
int maxfd = 0;
int bytes;
if (!max_fd_size) max_fd_size = getdtablesize ();
bytes = howmany (max_fd_size, NFDBITS);
rset = (fd_set *) alloca (bytes);
wset = (fd_set *) alloca (bytes);
xset = (fd_set *) alloca (bytes);
/* We can't call FD_ZERO, since FD_ZERO only works with sets of exactly FD_SETSIZE size. */
bzero (rset, bytes);
bzero (wset, bytes);
bzero (xset, bytes);
for (f = fds; f < &fds[nfds]; ++f)
{
f->revents = 0;
if (f->fd >= 0)
{
if (f->fd >= max_fd_size)
{
/* The user provides a file descriptor number which is higher than the maximum we got from the `getdtablesize' call. Maybe this is ok so enlarge the arrays. */
fd_set *nrset, *nwset, *nxset;
int nbytes;
max_fd_size = roundup (f->fd, NFDBITS);
nbytes = howmany (max_fd_size, NFDBITS);
nrset = (fd_set *) alloca (nbytes);
nwset = (fd_set *) alloca (nbytes);
nxset = (fd_set *) alloca (nbytes);
bzero ((char *) nrset + bytes, nbytes - bytes);
bzero ((char *) nwset + bytes, nbytes - bytes);
bzero ((char *) nxset + bytes, nbytes - bytes);
rset = (fd_set *) memcpy (nrset, rset, bytes);
wset = (fd_set *) memcpy (nwset, wset, bytes);
xset = (fd_set *) memcpy (nxset, xset, bytes);
bytes = nbytes;
}
if (f->events & POLLIN) FD_SET (f->fd, rset);
if (f->events & POLLOUT) FD_SET (f->fd, wset);
if (f->events & POLLPRI) FD_SET (f->fd, xset);
if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) maxfd = f->fd;
}
}
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
while (true)
{
ready = select (maxfd + 1, rset, wset, xset, timeout == -1 ? NULL : &tv);
/* It might be that one or more of the file descriptors is invalid. We now try to find and mark them and then try again. */
if (ready == -1 && errno == EBADF)
{
fd_set *sngl_rset = (fd_set *) alloca (bytes);
fd_set *sngl_wset = (fd_set *) alloca (bytes);
fd_set *sngl_xset = (fd_set *) alloca (bytes);
struct timeval sngl_tv;
/* Clear the original set. */
bzero (rset, bytes);
bzero (wset, bytes);
bzero (xset, bytes);
/* This means we don't wait for input. */
sngl_tv.tv_sec = 0;
sngl_tv.tv_usec = 0;
maxfd = -1;
/* Reset the return value. */
ready = 0;
for (f = fds; f < &fds[nfds]; ++f)
if (f->fd != -1 && (f->events & (POLLIN|POLLOUT|POLLPRI)) && (f->revents & POLLNVAL) == 0)
{
int n;
bzero (sngl_rset, bytes);
bzero (sngl_wset, bytes);
bzero (sngl_xset, bytes);
if (f->events & POLLIN) FD_SET (f->fd, sngl_rset);
if (f->events & POLLOUT) FD_SET (f->fd, sngl_wset);
if (f->events & POLLPRI) FD_SET (f->fd, sngl_xset);
n = select (f->fd + 1, sngl_rset, sngl_wset, sngl_xset, &sngl_tv);
if (n != -1)
{
/* This descriptor is ok. */
if (f->events & POLLIN) FD_SET (f->fd, rset);
if (f->events & POLLOUT) FD_SET (f->fd, wset);
if (f->events & POLLPRI) FD_SET (f->fd, xset);
if (f->fd > maxfd) maxfd = f->fd;
if (n > 0) ++ready; /* Count it as being available. */
}
else if (errno == EBADF)
f->revents |= POLLNVAL;
}
/* Try again. */
continue;
}
break;
}
if (ready > 0) for (f = fds; f < &fds[nfds]; ++f)
{
if (f->fd >= 0)
{
if (FD_ISSET (f->fd, rset)) f->revents |= POLLIN;
if (FD_ISSET (f->fd, wset)) f->revents |= POLLOUT;
if (FD_ISSET (f->fd, xset)) f->revents |= POLLPRI;
}
}
return ready;
}
#endif
BPoll::BPoll(){
ofdsNum = 0;
ofds = 0;
ofdsNext = 0;
}
BPoll::~BPoll(){
if(ofds)
free(ofds);
ofds = 0;
ofdsNum = 0;
ofdsNext = 0;
}
void BPoll::append(int fd, int events){
ofds = (PollFd*)realloc(ofds, (ofdsNum + 1) * sizeof(PollFd));
ofds[ofdsNum].fd = fd;
ofds[ofdsNum].events = events;
ofds[ofdsNum].revents = 0;
ofdsNum++;
}
void BPoll::delFd(int fd){
int n;
for(n = 0; n < ofdsNum; n++){
if(ofds[n].fd == fd){
break;
}
}
if((n + 1) < ofdsNum)
memcpy(&ofds[n], &ofds[n + 1], (ofdsNum - (n + 1)) * sizeof(PollFd));
if(n == ofdsNext)
ofdsNext = 0;
ofdsNum--;
ofds = (PollFd*)realloc(ofds, ofdsNum * sizeof(PollFd));
}
int BPoll::nextFd(int i){
i++;
if(i >= ofdsNum)
i = 0;
return i;
}
int BPoll::getPollFdsNum(){
return ofdsNum;
}
BPoll::PollFd* BPoll::getPollFds(){
return ofds;
}
BError BPoll::doPoll(int& fd, int timeoutUs){
BError err;
int timeoutMs;
int r;
int i;
if(timeoutUs >= 0)
timeoutMs = timeoutUs / 1000;
else
timeoutMs = -1;
r = poll(ofds, ofdsNum, timeoutMs);
if(r > 0){
// Get next available fd in round robin fashion
for(i = ofdsNext; ;){
if(ofds[i].revents){
fd = ofds[i].fd;
ofdsNext = nextFd(i);
break;
}
i = nextFd(i);
if(i == ofdsNext)
break;
}
}
else if(r < 0){
err.set(-errno, strerror(errno));
fd = -errno;
}
else if(r == 0){
err.set(-ETIMEDOUT, "Timeout");
fd = -ETIMEDOUT;
}
return err;
}
void BPoll::clear(){
if(ofds)
free(ofds);
ofds = 0;
ofdsNum = 0;
ofdsNext = 0;
}