home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / pty4 / part07 / sigsched.c < prev   
Encoding:
C/C++ Source or Header  |  1992-02-18  |  13.0 KB  |  602 lines

  1. /* sigsched.c, sigsched.h: signal-schedule thread library
  2. Daniel J. Bernstein, brnstnd@nyu.edu.
  3. Depends on ralloc.h, sod.h, config/fdsettrouble.h.
  4. Requires BSDish environment: reliable signals, sig{vec,block,setmask}, select.
  5. 9/1/91: Added worst-case fdset, FD_ZERO, etc. definitions.
  6. 8/25/91: sigsched 1.1, public domain.
  7. 8/25/91: Fixed bug that sigs[sched->blah].r didn't force instant timeout.
  8. 8/25/91: Fixed bug that if select() returned -1 then fds were still checked.
  9. 7/21/91: Changed forever to a 1-hour wakeup.
  10. 7/19/91: Added isopen() to fix bug in case of bad descriptor.
  11. 7/18/91: Baseline. sigsched 1.0, public domain.
  12. No known patent problems.
  13.  
  14. Documentation in sigsched.3.
  15.  
  16. XXX: how well do we clean up upon ss_exec() exit?
  17.  
  18. */
  19.  
  20. #include <sys/types.h>
  21. #include <sys/param.h>
  22. #include <sys/time.h>
  23. #include <signal.h>
  24. #include <errno.h>
  25. #include <fcntl.h>
  26. extern int errno;
  27. #include "config/fdsettrouble.h"
  28. #include "sigsched.h"
  29. #include "ralloc.h"
  30. #include "sod.h"
  31.  
  32. /* XXX: should restore signal set exactly after ss_exec returns */
  33.  
  34. typedef int sigc_set; /*XXX */
  35.  
  36. #define sigc_ismember(x,i) (*(x) & (1 << ((i) - 1)))
  37. #define sigc_addset(x,i) (*(x) |= (1 << ((i) - 1)))
  38. #define sigc_emptyset(x) (*(x) = 0)
  39.  
  40. /*       sigprocmask(SIG_UNBLOCK,xxxx,(sigc_set *) 0); */
  41. #define sigc_unblock(x) (sigsetmask(sigblock(0) & ~*(x)))
  42. /*       sigprocmask(SIG_BLOCK,xxxx,(sigc_set *) 0); */
  43. #define sigc_block(x) (sigblock(*(x)))
  44.  
  45. #ifndef NSIG
  46. #define NSIG 64 /* it's not as if any sane system has more than 32 */
  47. #endif
  48.  
  49. #define NUMSIGS NSIG
  50.  
  51. #ifndef FD_SETSIZE
  52. #define FD_SETSIZE 256
  53. #endif
  54.  
  55. #define NUMFDS FD_SETSIZE /* if select() can't handle it, we can't either */
  56.  
  57. #ifdef LACKING_FD_ZERO
  58. #define NFDBITS    (sizeof(fd_mask) * NBBY)
  59. #define    FD_SET(n,p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
  60. #define    FD_ISSET(n,p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
  61. #define FD_ZERO(p) bzero((caddr_t)(p),sizeof(*(p)))
  62. #endif
  63.  
  64. #ifdef DESPERATION_FD_SET
  65. #undef NFDBITS
  66. #undef FD_SET
  67. #undef FD_ISSET
  68. #undef FD_ZERO
  69. #undef fd_set
  70. #define fd_set long
  71. #define FD_SET(n,p) ((*p) |= (1 << (n)))
  72. #define FD_ISSET(n,p) ((*p) & (1 << (n)))
  73. #define FD_ZERO(p) (*p = 0L)
  74. #endif
  75.  
  76. #define ASAP 1
  77. #define SIGNAL 2
  78. #define READ 3
  79. #define WRITE 4
  80. #define EXCEPT 5
  81. #define JUNK 6
  82. #define EXTERN 7
  83.  
  84. typedef struct { ss_sig s; int r; } ss_sigplus;
  85.  
  86. static ss_sigplus asap;
  87. static ss_sigplus sigs[NUMSIGS];
  88. static ss_sigplus reads[NUMFDS];
  89. static ss_sigplus writes[NUMFDS];
  90. static ss_sigplus excepts[NUMFDS];
  91. static ss_sigplus junk; /* special case for internal use */
  92.  
  93. static void initsigs()
  94. {
  95.  int i;
  96.  asap.s.type = ASAP; asap.s.u.n = 0; asap.r = 0;
  97.  for (i = 0;i < NUMSIGS;++i)
  98.   { sigs[i].s.type = SIGNAL; sigs[i].s.u.n = i; sigs[i].r = 0; }
  99.  for (i = 0;i < NUMFDS;++i)
  100.   { reads[i].s.type = READ; reads[i].s.u.n = i; reads[i].r = 0; }
  101.  for (i = 0;i < NUMFDS;++i)
  102.   { writes[i].s.type = WRITE; writes[i].s.u.n = i; writes[i].r = 0; }
  103.  for (i = 0;i < NUMFDS;++i)
  104.   { excepts[i].s.type = EXCEPT; excepts[i].s.u.n = i; excepts[i].r = 0; }
  105.  junk.s.type = JUNK; junk.s.u.n = 0; junk.r = 0;
  106. }
  107.  
  108. ss_sig *ss_asap()
  109. { return &(asap.s); }
  110. #define OKsig(i) ((i >= 0) && (i < NUMSIGS))
  111. ss_sig *ss_signal(i) int i;
  112. { if (!OKsig(i)) return 0; return &(sigs[i].s); }
  113. #define OKfd(fd) ((fd >= 0) && (fd < NUMFDS))
  114. ss_sig *ss_sigread(fd) int fd;
  115. { if (!OKfd(fd)) return 0; return &(reads[fd].s); }
  116. ss_sig *ss_sigwrite(fd) int fd;
  117. { if (!OKfd(fd)) return 0; return &(writes[fd].s); }
  118. ss_sig *ss_sigexcept(fd) int fd;
  119. { if (!OKfd(fd)) return 0; return &(excepts[fd].s); }
  120.  
  121. void ss_externsetsig(sig,x)
  122. ss_sig *sig;
  123. ss_extern *x;
  124. {
  125.  sig->type = EXTERN;
  126.  sig->u.c = (char *) x;
  127. }
  128.  
  129. struct sched
  130.  {
  131.   ss_sig *sig;
  132.   ss_thread *t;
  133.   union { ss_id i; ss_idptr p; } id;
  134.   int flagi;
  135.   int wait;
  136.  }
  137. ;
  138.  
  139. SODdecl(schedlist,struct sched);
  140.  
  141. static schedlist schedhead = 0;
  142. static int schednum = 0;
  143. static int schedjunked = 0;
  144. static int numwait = 0;
  145.  
  146. void ss_forcewait()
  147. {
  148.  ++numwait;
  149. }
  150.  
  151. void ss_unforcewait()
  152. {
  153.  --numwait;
  154. }
  155.  
  156. int ss_schedvwait(sig,t,flagi,i,p,wait)
  157. ss_sig *sig;
  158. ss_thread *t;
  159. int flagi;
  160. ss_id i;
  161. ss_idptr p;
  162. int wait;
  163. {
  164.  schedlist s;
  165.  
  166.  if (sig->type == EXTERN)
  167.   {
  168.    ss_extern *x;
  169.    x = (ss_extern *) sig->u.c;
  170.    return x->sched(x,t,flagi,i,p,wait);
  171.   }
  172.  s = SODalloc(schedlist,s,ralloc);
  173.  if (!s)
  174.    return -1;
  175.  SODdata(s).sig = sig;
  176.  SODdata(s).t = t;
  177.  if (SODdata(s).flagi = flagi)
  178.    SODdata(s).id.i = i;
  179.  else
  180.    SODdata(s).id.p = p;
  181.  SODdata(s).wait = wait;
  182.  SODpush(schedhead,s);
  183.  ++schednum;
  184.  if (wait)
  185.    ++numwait;
  186.  return 0;
  187. }
  188.  
  189. int ss_schedwait(sig,t,i,wait)
  190. ss_sig *sig;
  191. ss_thread *t;
  192. ss_id i;
  193. int wait;
  194. {
  195.  return ss_schedvwait(sig,t,1,i,(ss_idptr) 0,wait);
  196. }
  197.  
  198. int ss_sched(sig,t,i)
  199. ss_sig *sig;
  200. ss_thread *t;
  201. ss_id i;
  202. {
  203.  return ss_schedvwait(sig,t,1,i,(ss_idptr) 0,0);
  204. }
  205.  
  206. struct oncestuff { ss_sig *sig; ss_thread *t; ss_id i; } ;
  207. /* XXX: this is the same as some other struct */
  208.  
  209. static void once(p)
  210. ss_idptr p;
  211. {
  212.  struct oncestuff *os;
  213.  os = (struct oncestuff *) p;
  214.  if (ss_unschedv(os->sig,once,0,0,p) == -1)
  215.    ; /* impossible */
  216.  os->t(os->i);
  217.  RFREE(os);
  218. }
  219.  
  220. int ss_schedonce(sig,t,i)
  221. ss_sig *sig;
  222. ss_thread *t;
  223. ss_id i;
  224. {
  225.  struct oncestuff *os;
  226.  
  227.  os = (struct oncestuff *) ralloc(sizeof(struct oncestuff));
  228.  if (!os)
  229.    return -1;
  230.  os->sig = sig; os->t = t; os->i = i;
  231.  return ss_schedvwait(sig,once,0,0,(ss_idptr) os,1);
  232. }
  233.  
  234. /* XXX: could rallocinstall() this, if it has the recvhead() test */
  235.  
  236. static int schedcleanup()
  237. {
  238.  schedlist s;
  239.  schedlist t;
  240.  schedlist sprev;
  241.  
  242. /*  if (recvhead) return 0;  XXX: needs recvhead in scope */
  243.  
  244.  if (!schedjunked)
  245.    return 0;
  246.  
  247.  sprev = 0;
  248.  s = schedhead;
  249.  while (s)
  250.   {
  251.    if (SODdata(s).sig == &(junk.s))
  252.     {
  253.      if (sprev)
  254.       {
  255.        SODpop(SODnext(sprev),t); /* XXX: not part of official sod interface */
  256.        s = SODnext(sprev);
  257.       }
  258.      else
  259.       {
  260.        SODpop(s,t);
  261.        schedhead = s;
  262.       }
  263.      SODfree(t,rfree);
  264.      --schednum;
  265.      --schedjunked;
  266.     }
  267.    else
  268.     {
  269.      sprev = s;
  270.      s = SODnext(s);
  271.     }
  272.   }
  273.  
  274. /* schednum -= schedjunked; now done dynamically inside loop */
  275. /* schedjunked = 0; */
  276.  return 1;
  277. }
  278.  
  279. static void nothing(id)
  280. ss_id id;
  281. {
  282.  ;
  283. }
  284.  
  285. int ss_unschedv(sig,t,flagi,i,p)
  286. ss_sig *sig;
  287. ss_thread *t;
  288. int flagi;
  289. ss_id i;
  290. ss_idptr p;
  291. {
  292.  schedlist s;
  293.  
  294.  if (sig->type == EXTERN)
  295.   {
  296.    ss_extern *x;
  297.    x = (ss_extern *) sig->u.c;
  298.    return x->unsched(x,t,flagi,i,p);
  299.   }
  300.  for (s = schedhead;s;s = SODnext(s))
  301.    if (SODdata(s).sig == sig && SODdata(s).t == t)
  302.      if (SODdata(s).flagi == flagi)
  303.        if (flagi ? (SODdata(s).id.i == i) : (SODdata(s).id.p == p))
  304.         {
  305.          SODdata(s).sig = &(junk.s);
  306.          SODdata(s).t = nothing; /* just in case */
  307.          if (SODdata(s).wait)
  308.            --numwait;
  309.          SODdata(s).wait = 0;
  310.          ++schedjunked;
  311.          return 0;
  312.         }
  313.  return 1;
  314. }
  315.  
  316. int ss_unsched(sig,t,i)
  317. ss_sig *sig;
  318. ss_thread *t;
  319. ss_id i;
  320. {
  321.  return ss_unschedv(sig,t,1,i,(ss_idptr) 0);
  322. }
  323.  
  324. static struct timeval timeout;
  325. static struct timeval instant = { 0, 0 };
  326. static struct timeval forever = { 3600, 0 };
  327.   /* XXX: talk to me */
  328.  
  329. static void handle(i)
  330. int i;
  331. {
  332.  timeout = instant; /* XXX: structure copying */
  333.  sigs[i].r = 1;
  334. }
  335.  
  336. static sigc_set sigstorage;
  337. static sigc_set *xxxx = 0;
  338.  
  339. int ss_addsig(i)
  340. int i;
  341. {
  342.  if (!OKsig(i))
  343.    return -1;
  344.  if (!xxxx)
  345.   {
  346.    xxxx = &sigstorage;
  347.    sigc_emptyset(xxxx);
  348.   }
  349.  sigc_addset(xxxx,i);
  350.  return 0;
  351. }
  352.  
  353. static int isopen(fd)
  354. int fd;
  355. {
  356.  /* XXX: should call this only if select() fails */
  357.  return fcntl(fd,F_GETFL,0) != -1;
  358. }
  359.  
  360. SODdecl(recvlist,schedlist);
  361.  
  362. int ss_exec()
  363. {
  364.  int i;
  365.  struct sigvec sv;
  366.  recvlist recvhead;
  367.  recvlist temp;
  368.  schedlist sch;
  369.  
  370.  initsigs();
  371.  
  372.  if (xxxx)
  373.   {
  374.    sigc_block(xxxx);
  375.  
  376.    sv.sv_handler = handle;
  377.    sv.sv_mask = *xxxx; /* so handle won't interrupt itself */
  378.    sv.sv_flags = 0;
  379.  
  380.    /* XXX: Does anyone but me find it absolutely idiotic that POSIX
  381.       doesn't provide a way to get each member of a signal set in turn? */
  382.    for (i = 0;i < NUMSIGS;i++)
  383.     {
  384.      if (sigc_ismember(xxxx,i))
  385.        if (sigvec(i,&sv,(struct sigvec *) 0) == -1) /*XXX: really trash orig? */
  386.      ; /* not our problem */
  387.     }
  388.   }
  389.  
  390.  recvhead = 0;
  391.  
  392.  while (numwait)
  393.   {
  394.    if (recvhead)
  395.     {
  396.      int w;
  397.      SODpop(recvhead,temp);
  398.      sch = SODdata(temp);
  399.  
  400. /* This is the only section where we call user code. */
  401. #define DOIT \
  402. if (SODdata(sch).flagi) \
  403.   SODdata(sch).t(SODdata(sch).id.i); \
  404. else \
  405.   SODdata(sch).t(SODdata(sch).id.p);
  406.  
  407.      switch(SODdata(sch).sig->type)
  408.       {
  409.        case JUNK:
  410.      break; /* has been unscheduled while waiting on the receive list */
  411.        case ASAP:
  412.      DOIT
  413.      break;
  414.        case READ:
  415.      if (reads[w = SODdata(sch).sig->u.n].r)
  416.        DOIT
  417.      reads[w].r = 0;
  418.      break;
  419.        case WRITE:
  420.      if (writes[w = SODdata(sch).sig->u.n].r)
  421.        DOIT
  422.      writes[w].r = 0;
  423.      break;
  424.        case EXCEPT:
  425.      if (excepts[w = SODdata(sch).sig->u.n].r)
  426.        DOIT
  427.      excepts[w].r = 0;
  428.      break;
  429.        case SIGNAL:
  430.      if (sigs[w = SODdata(sch).sig->u.n].r)
  431.        DOIT
  432.      sigs[w].r = 0;
  433.        /* ``after the end of the last...'' */
  434.      break;
  435.        case EXTERN:
  436.      /* by definition, an external library handles this */
  437.      break;
  438.        default: /* XXX: huh? */
  439.      ;
  440.       }
  441.      SODfree(temp,rfree);
  442.     }
  443.    else
  444.     {
  445.      schedlist sp;
  446.      static fd_set rfds;
  447.      static fd_set wfds;
  448.      static fd_set efds;
  449.      static int maxfd;
  450.      int r;
  451.  
  452.      if (schedjunked > 100)
  453.        if (schednum / schedjunked < 3)
  454.          (void) schedcleanup(); /* now's as good a time as any */
  455.  
  456.      timeout = forever;
  457.      FD_ZERO(&rfds);
  458.      FD_ZERO(&wfds);
  459.      FD_ZERO(&efds);
  460.      maxfd = -1;
  461.  
  462.      for (sp = schedhead;sp;sp = SODnext(sp))
  463.       {
  464.        switch(SODdata(sp).sig->type)
  465.     {
  466.      case JUNK:
  467.        break;
  468.      case ASAP:
  469.        timeout = instant;
  470.        break;
  471.      case SIGNAL:
  472.        if (sigs[SODdata(sp).sig->u.n].r)
  473.          timeout = instant;
  474.        break;
  475.      case READ:
  476.        if (isopen(SODdata(sp).sig->u.n))
  477.          FD_SET(SODdata(sp).sig->u.n,&rfds);
  478.        if (SODdata(sp).sig->u.n > maxfd)
  479.          maxfd = SODdata(sp).sig->u.n;
  480.        break;
  481.      case WRITE:
  482.        if (isopen(SODdata(sp).sig->u.n))
  483.          FD_SET(SODdata(sp).sig->u.n,&wfds);
  484.        if (SODdata(sp).sig->u.n > maxfd)
  485.          maxfd = SODdata(sp).sig->u.n;
  486.        break;
  487.      case EXCEPT:
  488.        if (isopen(SODdata(sp).sig->u.n))
  489.          FD_SET(SODdata(sp).sig->u.n,&efds);
  490.        if (SODdata(sp).sig->u.n > maxfd)
  491.          maxfd = SODdata(sp).sig->u.n;
  492.        break;
  493.      case EXTERN:
  494.        break;
  495.      default: /*XXX: huh? */
  496.        break;
  497.     }
  498.       }
  499.  
  500.      if (xxxx)
  501.        sigc_unblock(xxxx);
  502.      /* This is the only section where handle() can be called. */
  503.      /* XXX: If maxfd == -1, this select functions as a pause. */
  504.      /* XXX: If maxfd == -1 and timeout is instant, should skip select. */
  505.      /* XXX: Random bug of note: Real BSD systems will say that the
  506.         fd is writable as soon as a network connect() fails. The first
  507.     I/O will show the error (though it's rather stupid that you
  508.     can't find out the error without doing I/O). What does Ultrix
  509.     4.1 do? It pauses for 75 seconds. Dolts. */
  510.      r = select(maxfd + 1,&rfds,&wfds,&efds,&timeout);
  511.        /* XXX: does this necessarily prevent timeout race conditions? */
  512.      if (xxxx)
  513.        sigc_block(xxxx);
  514.  
  515.      if (r == -1)
  516.       {
  517.        FD_ZERO(&rfds);
  518.        FD_ZERO(&wfds);
  519.        FD_ZERO(&efds);
  520.        switch(errno)
  521.         {
  522.          case EINTR: /* fine, this will happen on any signal */ break;
  523.          case EBADF: /* who knows? */ break;
  524.          case EINVAL: /* simply impossible */ break;
  525.          default: /*XXX*/ ;
  526.      /* well, that was real useful */
  527.         }
  528.       }
  529.  
  530.      for (sp = schedhead;sp;sp = SODnext(sp))
  531.       {
  532.        switch(SODdata(sp).sig->type) 
  533.     {
  534.      case JUNK:
  535.        break;
  536.      case ASAP:
  537.            temp = SODalloc(recvlist,temp,ralloc);
  538.            if (!temp)
  539.              return -1; /*XXX*/
  540.        SODdata(temp) = sp;
  541.            SODpush(recvhead,temp);
  542.        break;
  543.      case SIGNAL:
  544.        if (sigs[SODdata(sp).sig->u.n].r)
  545.         {
  546.              temp = SODalloc(recvlist,temp,ralloc);
  547.              if (!temp)
  548.                return -1; /*XXX*/
  549.          SODdata(temp) = sp;
  550.              SODpush(recvhead,temp);
  551.         }
  552.        break;
  553.      case READ:
  554.        if (FD_ISSET(SODdata(sp).sig->u.n,&rfds))
  555.         {
  556.          FD_CLR(SODdata(sp).sig->u.n,&rfds);
  557.          reads[SODdata(sp).sig->u.n].r = 1;
  558.              temp = SODalloc(recvlist,temp,ralloc);
  559.              if (!temp)
  560.                return -1; /*XXX*/
  561.          SODdata(temp) = sp;
  562.              SODpush(recvhead,temp);
  563.         }
  564.        break;
  565.      case WRITE:
  566.        if (FD_ISSET(SODdata(sp).sig->u.n,&wfds))
  567.         {
  568.          FD_CLR(SODdata(sp).sig->u.n,&wfds);
  569.          writes[SODdata(sp).sig->u.n].r = 1;
  570.              temp = SODalloc(recvlist,temp,ralloc);
  571.              if (!temp)
  572.                return -1; /*XXX*/
  573.          SODdata(temp) = sp;
  574.              SODpush(recvhead,temp);
  575.         }
  576.        break;
  577.      case EXCEPT:
  578.        if (FD_ISSET(SODdata(sp).sig->u.n,&efds))
  579.         {
  580.          FD_CLR(SODdata(sp).sig->u.n,&efds);
  581.          excepts[SODdata(sp).sig->u.n].r = 1;
  582.              temp = SODalloc(recvlist,temp,ralloc);
  583.              if (!temp)
  584.                return -1; /*XXX*/
  585.          SODdata(temp) = sp;
  586.              SODpush(recvhead,temp);
  587.         }
  588.        break;
  589.      case EXTERN:
  590.        break;
  591.      default:
  592.        break;
  593.     }
  594.       }
  595.     }
  596.   }
  597.  if (xxxx)
  598.    sigc_unblock(xxxx);
  599.    /* XXX: should put this at other returns as well */
  600.  return 0;
  601. }
  602.