//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//       cdevSync class (abstract base class)
//
// Author:  Jie Chen & Chip Watson
//
// Revision History:
//   cdevSync.cc,v
// Revision 1.9  1997/02/18  15:45:42  chen
// port to linux 2.0.x + addUserFdCallback
//
// Revision 1.8  1997/01/09  16:32:04  akers
// Altered attachReadFd and detachReadFd to return CDEV_WARNING if they are already attached or detached prior to the call
//
// Revision 1.7  1996/06/26  15:54:25  akers
// Modifications to support multiple OS Versions
//
// Revision 1.6  1995/11/08  14:44:42  chen
// fix for irix5.3
//
// Revision 1.5  1995/11/07  19:52:24  chen
// change HP_UX flag to hpux
//
// Revision 1.4  1995/10/26  14:33:31  akers
// Removal of Warnings
//
// Revision 1.3  1995/10/05  18:40:16  chen
// VMS fixes
//
// Revision 1.2  1995/08/22  15:32:15  akers
// Removed assertion from the cdevSync::dispatch function
//
// Revision 1.1.1.1  1995/06/16  17:14:08  epics
// initial import of cdev
//
//
#include <errno.h>
#include <cdevClock.h>
#include <cdevErrCode.h>
#include "cdevSync.h"

#ifdef __VMS
#ifdef _TGV_MULTINET
#include <sys/uio.h>     // for sys/socket.h
#include <sys/socket.h>  // for select() proto()
#endif // _TGV_MULTINET
#endif // __VMS defined.

#ifdef sgi
#include <unistd.h>
#endif

cdevSync::cdevSync (void)
{
#ifdef _TRACE_OBJECTS
  printf("Create abstract class cdevSync\n");
#endif
}

cdevSync::~cdevSync (void)
{
#ifdef _TRACE_OBJECTS
  printf("Destroy abstract class cdevSync\n");
#endif
}  

int
cdevSync::attachReadFd (int fd)
{
  if(!rdMask_.is_set(fd))  
    {
    rdMask_.set_bit (fd);
    return CDEV_SUCCESS;
    }
  return CDEV_WARNING;
}

int
cdevSync::detachReadFd (int fd)
{
  if(rdMask_.is_set(fd))
    {
    rdMask_.clr_bit (fd);
    return CDEV_SUCCESS;
    }
  return CDEV_WARNING;
}

int
cdevSync::handleEvents (void)
{
  return handleEvents (0);
}

int 
cdevSync::handleEvents (cdevTimeValue *tv)
{
  cdevFdSet rmaskret;
  cdevFdSet wmaskret;
  cdevFdSet emaskret;
  int       nfound = 0, status = 1;
  cdevClock timer;

  if (tv)
    timer.schedule (0, *tv);

  nfound = waitFor (rmaskret, wmaskret, emaskret, tv);
  // really handle lower level pend here
  dispatch (nfound, rmaskret, wmaskret, emaskret);

  if (tv) { // finite time value
    while (!timer.expired()){
      cdevTimeValue newTv = timer.scheduledTime() - cdevClock::currentTime();
      nfound = waitFor (rmaskret, wmaskret, emaskret, &newTv);
      if (nfound > 0)
	status = 0;
      // really handle lower level pend here
      dispatch (nfound, rmaskret, wmaskret, emaskret);
    }
  }
  // status == 0 stands for there is at least one IO event
  if (status == 0)
    return CDEV_SUCCESS;
  else
    return CDEV_TIMEOUT;
}

int 
cdevSync::waitFor (cdevFdSet &rmaskret,
		   cdevFdSet &wmaskret,
		   cdevFdSet &emaskret,
		   cdevTimeValue *how_long)
{
  int nfound = 0;
  
  // ***************************************************************************
  // * This code added to protect against NULL cdevTimeValue being submitted
  // * to the function.
  // ***************************************************************************
  timeval tv = (how_long?*how_long:cdevTimeValue(60,0));
  
  do {
    int maxfd = maxHandle (rdMask_.max_set(), 
			   wrMask_.max_set(), 
			   exMask_.max_set() ) + 1;

    // every time one calls select rmaskret or similiar will change.
    // all the masks are cleared when the timer expires
    rmaskret   = rdMask_;
    wmaskret   = wrMask_;
    emaskret   = exMask_;
    
#if !defined(__hpux) || (defined(__hpux) && _OS_MAJOR_>9)
    nfound = ::select (maxfd, (fd_set *)rmaskret,
		       (fd_set *)wmaskret, (fd_set *)emaskret,
		       &tv);
#else
    nfound = ::select (maxfd, (int *)(fd_set *)rmaskret,
		       (int *)(fd_set *)wmaskret,
		       (int *)(fd_set *)emaskret,
		       &tv);
#endif
  } // select system may well be interrupted by signals, check!!!
  while (nfound == -1 && handleError () > 0 );
  return nfound;
}

void
cdevSync::dispatch (int nfound,
		    cdevFdSet &rmaskret,
		    cdevFdSet & /* wmaskret */,
		    cdevFdSet & /* emaskret */)
{
  int i;

  if (nfound > 0)
    for (cdevFdSet_Iterator fdir (rmaskret); 
	 (i = fdir ()) != -1 && --nfound >= 0;
	 ++fdir){
      notifyService (i);
    }
}

int
cdevSync::handleError (void)
{
#ifdef _WIN32
  // check interrupt
  int wsaerr = WSAGetLastError ();
  if (wsaerr == WSAEINTR)
    return 1;
  // check bad file descriptors
  if (wsaerr == WSAEBADF)
    return checkFds ();
  return -1;
#else
  // check interrupt
  if (errno == EINTR)
    return 1;
  // check bad file descriptors
  if (errno == EBADF)
    return checkFds ();
  return -1;
#endif
}

int
cdevSync::checkFds (void)
{
  cdevFdSet          rmask;
  cdevTimeValue      time_poll; 
  timeval            tv = time_poll;        
  cdevFdSet_Iterator ite (rdMask_);
  int                i;

  for (; (i = ite()) != -1; ++ite){
    rmask.set_bit (i);
#if !defined(__hpux) || (defined(__hpux) && _OS_MAJOR_>9)
    if(::select (i+1, (fd_set *)rmask, 0, 0, &tv) < 0)
#else
    if(::select (i+1, (int *)(fd_set *)rmask, 
		 0, 0, &tv) < 0)
#endif
      detachReadFd (i);
    rmask.clr_bit (i);
  }
  return 1;
}

int
cdevSync::checkFd (int fd)
{
  cdevFdSet          rmask;
  cdevTimeValue      time_poll; 
  timeval            tv = time_poll;
  
  rmask.set_bit (fd);
#if !defined(__hpux) || (defined(__hpux) && _OS_MAJOR_>9)
  if(::select (fd + 1, (fd_set *)rmask, 0, 0, &tv) < 0)
    return -1;
#else
  if(::select (fd + 1, (int *)(fd_set *)rmask, 
	       0, 0, &tv) < 0)
    return -1;
#endif
      
  return 0;
}


void
cdevSync::notifyService (int /* fd */)
{
  // empty, derived class give real implementation
  return;
}

int
cdevSync::maxHandle (int i, int j, int k)
{
  int t = i < j ? j : i;

  return k < t ? t : k;
}
