home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ixemul-45.0-src.tgz / tar.out / contrib / ixemul / library / select.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  10KB  |  314 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *
  5.  *  This library is free software; you can redistribute it and/or
  6.  *  modify it under the terms of the GNU Library General Public
  7.  *  License as published by the Free Software Foundation; either
  8.  *  version 2 of the License, or (at your option) any later version.
  9.  *
  10.  *  This library is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  *  Library General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU Library General Public
  16.  *  License along with this library; if not, write to the Free
  17.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *  select.c,v 1.1.1.1 1994/04/04 04:30:33 amiga Exp
  20.  *
  21.  *  select.c,v
  22.  * Revision 1.1.1.1  1994/04/04  04:30:33  amiga
  23.  * Initial CVS check in.
  24.  *
  25.  *  Revision 1.2  1993/11/05  22:00:59  mwild
  26.  *  extensively rewritten to work better along with inet.library
  27.  *
  28.  *  Revision 1.1  1992/05/14  19:55:40  mwild
  29.  *  Initial revision
  30.  *
  31.  */
  32.  
  33. #define _KERNEL
  34. #include "ixemul.h"
  35. #include "kprintf.h"
  36.  
  37. #include <sys/time.h>
  38. #include <unistd.h>
  39. #include <string.h>
  40.  
  41. #include "select.h"
  42.  
  43. #define __time_req (u.u_time_req)
  44. #define __tport    (u.u_sync_mp)
  45.  
  46. static void handle_select_port(void)
  47. {
  48.   struct StandardPacket *prw;
  49.  
  50.   while ((prw = GetPacket(u.u_select_mp)))
  51.       prw->sp_Pkt.dp_Port = 0;
  52. }
  53.  
  54. static void inline setcopy(int nfd, u_int *ifd, u_int *ofd)
  55. {
  56.   /* this procedure is here, because it's "normal" that if you only
  57.    * want to select on fd 0,1,2 eg. you only pass a long to select,
  58.    * not a whole fd_set, so we can't simply copy over results in the
  59.    * full size of an fd_set.. */
  60.   
  61.   /* we have to copy that many longs... */
  62.   nfd = (nfd+31) >> 5;
  63.   while (nfd--) *ofd++ = *ifd++;
  64. }
  65.  
  66. int
  67. aselect(int nfd, fd_set *ifd, fd_set *ofd, fd_set *efd, struct timeval *timeout, long mask)
  68. {
  69.   struct file *f;
  70.   int i, waitin, waitout, waitexc, dotout;
  71.   int result, ostat, sigio;
  72.   u_int wait_sigs;
  73.   struct timeval end_time;
  74.   int skipped_wait;
  75.   struct user *p = &u;
  76.   u_long recv_wait_sigs = 0;
  77.   u_long net_nfds;
  78.  
  79.   if (CURSIG (p))
  80.     {
  81.       *(p->u_errno) = EINTR;
  82.       return -1;
  83.     }
  84.  
  85.   /* as long as I don't support anything similar to a network, I surely
  86.    * won't get any exceptional conditions, so *efd is mapped into 
  87.    * *ifd, if it's set. */
  88.    
  89.   /* first check, that all included descriptors are valid and support
  90.    * the requested operation. If the user included a request to wait
  91.    * for a descriptor to be ready to read, while the descriptor was only
  92.    * opened for writing, the requested bit is immediately cleared
  93.    */
  94.   waitin = waitout = waitexc = 0;
  95.   if (nfd > NOFILE) nfd = NOFILE;
  96.  
  97.   for (i = 0; i < nfd; i++)
  98.     {
  99.       if (ifd && FD_ISSET(i, ifd) && (f = p->u_ofile[i]))
  100.     {
  101.       if (!f->f_read || !f->f_select)
  102.         FD_CLR(i, ifd);
  103.       else
  104.         ++waitin;
  105.     }
  106.       if (ofd && FD_ISSET(i, ofd) && (f = p->u_ofile[i]))
  107.     {
  108.       if (!f->f_write || !f->f_select)
  109.         FD_CLR(i, ofd);
  110.       else
  111.         ++waitout;
  112.     }
  113.       if (efd && FD_ISSET(i, efd) && (f = p->u_ofile[i]))
  114.     {
  115.       /* question: can an exceptional condition also occur on a 
  116.        * write-only fd?? */
  117.       if (!f->f_read || !f->f_select)
  118.         FD_CLR(i, efd);
  119.       else
  120.         ++waitexc;
  121.     }
  122.     }
  123.  
  124.   if ((dotout = (timeout && timerisset(timeout))))
  125.     {
  126.       /* remember the time we have to leave (timeout) */
  127.       syscall(SYS_gettimeofday, &end_time, 0);
  128.       end_time.tv_usec += timeout->tv_usec;
  129.       /* this conversion should be cheaper than a division and a modulo.. */
  130.       while (end_time.tv_usec >= 1000000)
  131.         {
  132.       end_time.tv_usec -= 1000000;
  133.       end_time.tv_sec++;
  134.     }
  135.       end_time.tv_sec += timeout->tv_sec;
  136.     }
  137.  
  138.   /* have to make sure we can clean up the timer-request ! */
  139.   ostat = p->p_stat;
  140.   p->p_stat = SSLEEP;
  141.   p->p_wchan = (caddr_t) select; /* will once be an own variable */
  142.   p->p_wmesg = "select";
  143.  
  144.   for (skipped_wait = 0; ; skipped_wait=1)
  145.     {
  146.       fd_set readyin, readyout, readyexc;
  147.       fd_set netin, netout, netexc;        /* used by ixnet.library */
  148.       int tout, readydesc, cmd;
  149.  
  150.       FD_ZERO(&readyin);
  151.       FD_ZERO(&readyout);
  152.       FD_ZERO(&readyexc);
  153.       
  154.       if (u.u_ixnetbase)
  155.         {
  156.           FD_ZERO(&netin);
  157.           FD_ZERO(&netout);
  158.           FD_ZERO(&netexc);
  159.           net_nfds = 0;
  160.         }
  161.  
  162.       tout = readydesc = 0;
  163.  
  164.       /* have to always wait for the `traditional' ^C and the library internal
  165.        * sleep_sig as well */
  166.       wait_sigs = SIGBREAKF_CTRL_C | (1 << p->u_sleep_sig) | mask;
  167.       
  168.       handle_select_port();
  169.  
  170.       if (skipped_wait)
  171.     {
  172.       cmd = SELCMD_CHECK;
  173.  
  174.       /* have all watched files get prepared for selecting */
  175.           for (i = 0; i < nfd; i++)
  176.         {
  177.           if (ifd && FD_ISSET (i, ifd) && (f = p->u_ofile[i]))
  178.             wait_sigs |= f->f_select (f, SELCMD_PREPARE, SELMODE_IN, &netin, &net_nfds);
  179.           if (ofd && FD_ISSET (i, ofd) && (f = p->u_ofile[i]))
  180.             wait_sigs |= f->f_select (f, SELCMD_PREPARE, SELMODE_OUT, &netout, &net_nfds);
  181.           if (efd && FD_ISSET (i, efd) && (f = p->u_ofile[i]))
  182.             wait_sigs |= f->f_select (f, SELCMD_PREPARE, SELMODE_EXC, &netexc, &net_nfds);
  183.         }
  184.       if (!(u.p_sigignore & sigmask (SIGIO)))
  185.         {
  186.           struct file **f = u.u_ofile;
  187.  
  188.           for (i = 0; i < u.u_lastfile; i++)
  189.             if (f[i] && (f[i]->f_flags & FASYNC) && !(ifd && FD_ISSET(i, ifd)))
  190.           wait_sigs |= f[i]->f_select (f[i], SELCMD_PREPARE, SELMODE_IN, &netin, &net_nfds);
  191.         }
  192.  
  193.       /* note: we never post a timeout request of less than SELTIMEOUT, so
  194.            * select() precision depends on SELTIMEOUT. However, considering that
  195.            * this call emulates a Unix syscall, this comes quite near to Unix
  196.            * precision, if not better ;-)) */
  197.       __time_req->tr_time.tv_sec = 0;
  198.       __time_req->tr_time.tv_usec = SELTIMEOUT;
  199.           __time_req->tr_node.io_Command = TR_ADDREQUEST;
  200.           SendIO((struct IORequest *)__time_req);
  201.           /* clear the bit, it's used for sync packets too, and might be set */
  202.           SetSignal (0, 1 << __tport->mp_SigBit);
  203.       wait_sigs |= 1 << __tport->mp_SigBit;
  204.  
  205.           /* now wait for all legally possible signals, this includes BSD
  206.            * signals (but want at least one signal set!) */
  207.       if (u.u_ixnetbase)
  208.             recv_wait_sigs = netcall(NET_waitselect, wait_sigs,
  209.              &netin, &netout, &netexc, net_nfds);
  210.       else
  211.             while (!(recv_wait_sigs = Wait (wait_sigs))) ;
  212.  
  213.       /* IMPORTANT: unqueue the timer request BEFORE polling the fd's,
  214.        *            or __wait_packet() will treat the timer request
  215.        *            as a packet... */
  216.  
  217.       if (! CheckIO ((struct IORequest *)__time_req))
  218.             AbortIO ((struct IORequest *)__time_req);
  219.           WaitIO ((struct IORequest *)__time_req);
  220.           
  221.           handle_select_port();
  222.         }
  223.       else
  224.     cmd = SELCMD_POLL;
  225.  
  226.       /* no matter what caused Wait() to return, wait for all requests to
  227.        * complete (we CAN'T abort a DOS packet, sigh..) */
  228.  
  229.       /* collect information from the file descriptors */
  230.       for (i = 0; i < nfd; i++)
  231.     {
  232.       if (ifd && FD_ISSET (i, ifd) && (f = p->u_ofile[i])
  233.           && f->f_select (f, cmd, SELMODE_IN, &netin, NULL))
  234.         {
  235.           FD_SET (i, &readyin);
  236.           ++ readydesc;
  237.         }
  238.       if (ofd && FD_ISSET (i, ofd) && (f = p->u_ofile[i])
  239.           && f->f_select (f, cmd, SELMODE_OUT, &netout, NULL))
  240.         {
  241.           FD_SET (i, &readyout);
  242.           ++ readydesc;
  243.         }
  244.       if (efd && FD_ISSET (i, efd) && (f = p->u_ofile[i])
  245.               && f->f_select (f, cmd, SELMODE_EXC, &netexc, NULL))
  246.         {
  247.           FD_SET (i, &readyexc);
  248.           ++ readydesc;
  249.         }
  250.     }
  251.  
  252.       /* we have a timeout condition, if readydesc == 0, dotout == 1 and 
  253.        * end_time < current time */
  254.       if (!readydesc && dotout)
  255.         {
  256.           struct timeval current_time;
  257.           
  258.           syscall(SYS_gettimeofday, ¤t_time, 0);
  259.           tout = timercmp (&end_time, ¤t_time, <);
  260.     }
  261.  
  262.       sigio = 0;
  263.       if (!(u.p_sigignore & sigmask (SIGIO)))
  264.         {
  265.           struct file **f = u.u_ofile;
  266.  
  267.           for (i = 0; i < u.u_lastfile; i++)
  268.             if (f[i] && (f[i]->f_flags & FASYNC) && !(ifd && FD_ISSET(i, ifd)))
  269.               if (f[i]->f_select (f[i], cmd, SELMODE_IN, &netin, NULL))
  270.                 sigio = 1;
  271.         }
  272.  
  273.       if (readydesc || tout || (timeout && !timerisset(timeout)))
  274.     {
  275.       if (ifd) setcopy(nfd, (u_int *)&readyin,  (u_int *)ifd);
  276.       if (ofd) setcopy(nfd, (u_int *)&readyout, (u_int *)ofd);
  277.       if (efd) setcopy(nfd, (u_int *)&readyexc, (u_int *)efd);
  278.       result = readydesc; /* ok for tout, since then readydesc is already 0 */
  279.       break;
  280.     }
  281.  
  282.       if (sigio || (recv_wait_sigs & (SIGBREAKF_CTRL_C | (1 << p->u_sleep_sig) | mask)))
  283.         {
  284.           result = -1;
  285.           break;
  286.         }
  287.     }
  288.  
  289.   p->p_wchan = 0;
  290.   p->p_wmesg = 0;
  291.   p->p_stat = ostat;
  292.   if (recv_wait_sigs == (u_long)-1)
  293.     return -1;
  294.   /* need special processing for ^C here, as that is completely disabled
  295.      when we're SSLEEPing */
  296.   if (recv_wait_sigs & SIGBREAKF_CTRL_C)
  297.     _psignal(FindTask(0), SIGINT);
  298.   if (sigio)
  299.     _psignal(FindTask(0), SIGIO);
  300.   setrun(FindTask(0));
  301.  
  302.   if (result == -1)
  303.     /* have to set this here, since errno can be changed in signal handlers */
  304.     *(p->u_errno) = EINTR;
  305.  
  306.   return result;
  307. }
  308.  
  309. int
  310. select(int nfd, fd_set *ifd, fd_set *ofd, fd_set *efd, struct timeval *timeout)
  311. {
  312.   return aselect(nfd, ifd, ofd, efd, timeout, 0);
  313. }
  314.