home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / CONTRIB / MBASE / MBASE50.TAR / mbase / src / lock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-21  |  12.7 KB  |  469 lines

  1. /*
  2.  * METALBASE 5.0
  3.  *
  4.  * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  5.  *                                       [ t-richj@microsoft.com ]
  6.  */
  7.  
  8. #define UTIL_C
  9. #include "mbase.h"
  10. #include "internal.h"
  11.  
  12. #ifdef LONGARGS
  13.    static mb_err     _set_hack  (relation *);
  14.    static void       _clr_hack  (relation *);
  15.    static void       _lck_pause (void);
  16.    static int        _is_dead   (relation *, int);
  17.    static void       _clrstrobe (relation *);
  18. #else
  19.    static mb_err     _set_hack();
  20.    static void       _clr_hack();
  21.    static void       _lck_pause();
  22.    static int        _is_dead();
  23.    static void       _clrstrobe();
  24. #endif
  25.  
  26. /*****************************************************************************
  27.  *
  28.  * LOCK TIMING
  29.  *
  30.  */
  31.  
  32. static void
  33. _lck_pause ()  /* Around .1 second pause, to reduce disk I/O */
  34. {
  35.    int  i, j = 1;
  36.    for (i = 0; i < 40000; i++)  /* UGLY!  CHANGE THIS! */
  37.       j = 1-j;
  38. }
  39.  
  40. /*****************************************************************************
  41.  *
  42.  * EXCLUSIVE (RELATION-WIDE) LOCKS
  43.  *
  44.  */
  45.  
  46. mb_err           /* Fair warning:                                            */
  47. _chk_elck (rel)  /* THIS ROUTINE CANNOT BE TRUSTED unless you have a         */
  48. relation  *rel;  /* temporary lock placed on the relation before calling it! */
  49. {
  50.    ushort pid;
  51.    if (rel->exc & 1)  baderr (MB_OKAY);  /* _we_ have the lock set */
  52.  
  53.    lseek (rel->lckcode, lckPOS_ELOCK, 0);
  54.    readx (rel->lckcode, &pid, 2);
  55.    if (! pid)  baderr (MB_OKAY);     /* or no one has the lock */
  56.  
  57.    baderr (MB_LOCKED);
  58. }
  59.  
  60. mb_err
  61. mb_unl   (rel)
  62. relation *rel;
  63. {
  64.    ushort pid;
  65.    if (_identify (rel) < 0)  reterr (MB_BAD_REL,  -1);
  66.    if (! (rel->exc & 1))     reterr (MB_OKAY, MB_OKAY); /* We didn't lock it */
  67.  
  68.    if (_set_lck (rel) != MB_OKAY)  baderr (mb_errno);
  69.  
  70.    lseek (rel->lckcode, lckPOS_ELOCK, 0);
  71.    readx (rel->lckcode, &pid,      2);
  72.  
  73.    if (pid == rel->pid)
  74.       {
  75.       pid = 0;
  76.       lseek (rel->lckcode, lckPOS_ELOCK, 0);
  77.       writx (rel->lckcode, &pid,      2);
  78.       }
  79.  
  80.    rel->exc &= 2;  /* Clear the exclusive-lock bit */
  81.    lckerr (rel, MB_OKAY, MB_OKAY);
  82. }
  83.  
  84. mb_err
  85. mb_lck   (rel)
  86. relation *rel;
  87. {
  88.    ushort pid;
  89.    if (_identify (rel) < 0)  reterr (MB_BAD_REL,  -1);
  90.    if (rel->exc & 1)         baderr (MB_OKAY);  /* We've already locked it */
  91.  
  92.    if (_set_lck (rel) != MB_OKAY)  baderr (mb_errno);
  93.  
  94.    lseek (rel->lckcode, lckPOS_ELOCK, 0);
  95.    readx (rel->lckcode, &pid, 2);
  96.    if (pid != 0)  lckerr (rel, MB_LOCKED, -1);
  97.  
  98.    lseek (rel->lckcode, lckPOS_ELOCK, 0);
  99.    writx (rel->lckcode, &rel->pid, 2);
  100.    rel->exc |= 1;  /* Set the exclusive-lock bit */
  101.  
  102.    lckerr (rel, MB_OKAY, MB_OKAY);
  103. }
  104.  
  105. /*****************************************************************************
  106.  *
  107.  * HACK LOCKS (CONCURRENCY CONTROL)
  108.  *
  109.  */
  110.  
  111. static void
  112. _clr_hack (rel)
  113. relation  *rel;
  114. {
  115.    ushort *pid;
  116.    char    pids[6];
  117.  
  118.    lseek (rel->lckcode, lckPOS_HLOCK, 0);
  119.    readx (rel->lckcode, pids, 6);
  120.  
  121.    if (*(pid = (ushort *)&pids[0]) == rel->pid)  *pid = 0L;
  122.    if (*(pid = (ushort *)&pids[2]) == rel->pid)  *pid = 0L;
  123.    if (*(pid = (ushort *)&pids[4]) == rel->pid)  *pid = 0L;
  124.  
  125.    lseek (rel->lckcode, lckPOS_HLOCK, 0);
  126.    writx (rel->lckcode, pids, 6);
  127. }
  128.  
  129. static mb_err
  130. _set_hack (rel)
  131. relation  *rel;
  132. {
  133.    ushort *pid;
  134.    char    pids[6];
  135.    int     fChange;
  136.    mb_time timeStart;
  137.  
  138.    timeStart = curtime();
  139.  
  140.    for (;;)
  141.       {
  142.       if (elap_t (timeStart) > 5)
  143.          {
  144.          pids[0] = pids[1] = pids[2] = pids[3] = pids[4] = pids[5] = 0;
  145.          lseek (rel->lckcode, lckPOS_HLOCK, 0);
  146.          writx (rel->lckcode, pids, 6);
  147.          timeStart = curtime();
  148.          continue;
  149.          }
  150.  
  151. /*
  152.  * FIRST ITERATION:
  153.  *
  154.  */
  155.  
  156.       fChange = 0;
  157.       lseek (rel->lckcode, lckPOS_HLOCK, 0);
  158.       readx (rel->lckcode, pids, 6);
  159.  
  160.       if (*(pid = (ushort *)&pids[0]) == rel->pid) { *pid = 0; fChange |= 1; }
  161.       if (*pid != 0)  fChange |= 2;
  162.       if (*(pid = (ushort *)&pids[2]) == rel->pid) { *pid = 0; fChange |= 1; }
  163.       if (*pid != 0)  fChange |= 2;
  164.       if (*(pid = (ushort *)&pids[4]) == rel->pid) { *pid = 0; fChange |= 1; }
  165.       if (*pid != 0)  fChange |= 2;
  166.  
  167.       if (! (fChange & 2))
  168.          {
  169.          *pid = rel->pid;  fChange |= 1;
  170.          }
  171.  
  172.       if (fChange & 1)
  173.          {
  174.          lseek (rel->lckcode, lckPOS_HLOCK, 0);
  175.          writx (rel->lckcode, pids, 6);
  176.          }
  177.  
  178.       if (fChange & 2)
  179.          {
  180.          continue;
  181.          }
  182.  
  183. /*
  184.  * SECOND ITERATION:
  185.  *
  186.  */
  187.  
  188.       lseek (rel->lckcode, lckPOS_HLOCK, 0);
  189.       readx (rel->lckcode, pids, 6);
  190.  
  191.       if (*(pid = (ushort *)&pids[0]) != 0)          continue; /* NOTE ORDER */
  192.       if (*(pid = (ushort *)&pids[4]) != rel->pid)   continue; /* NOTE ORDER */
  193.       if (*(pid = (ushort *)&pids[2]) != 0)          continue; /* NOTE ORDER */
  194.  
  195.       *pid = rel->pid;
  196.  
  197.       lseek (rel->lckcode, lckPOS_HLOCK, 0);
  198.       writx (rel->lckcode, pids, 6);
  199.  
  200. /*
  201.  * THIRD ITERATION:
  202.  *
  203.  */
  204.  
  205.       lseek (rel->lckcode, lckPOS_HLOCK, 0);
  206.       readx (rel->lckcode, pids, 6);
  207.  
  208.       if (*(pid = (ushort *)&pids[4]) != rel->pid)   continue; /* NOTE ORDER */
  209.       if (*(pid = (ushort *)&pids[2]) != rel->pid)   continue; /* NOTE ORDER */
  210.       if (*(pid = (ushort *)&pids[0]) != 0)          continue; /* NOTE ORDER */
  211.  
  212.       *pid = rel->pid;
  213.  
  214.       lseek (rel->lckcode, lckPOS_HLOCK, 0);
  215.       writx (rel->lckcode, pids, 6);
  216.  
  217. /*
  218.  * FINAL CHECK:
  219.  *
  220.  */
  221.  
  222.       lseek (rel->lckcode, lckPOS_HLOCK, 0);
  223.       readx (rel->lckcode, pids, 6);
  224.  
  225.       if (*(pid = (ushort *)&pids[4]) != rel->pid)   continue; /* NOTE ORDER */
  226.       if (*(pid = (ushort *)&pids[2]) != rel->pid)   continue; /* NOTE ORDER */
  227.       if (*(pid = (ushort *)&pids[0]) != rel->pid)   continue; /* NOTE ORDER */
  228.  
  229.       break;
  230.       }
  231.  
  232.    return MB_OKAY;
  233. }
  234.  
  235. /*****************************************************************************
  236.  *
  237.  * TEMPORARY LOCKS/ QUEUEING
  238.  *
  239.  */
  240.  
  241. mb_err
  242. _set_lck (rel)
  243. relation *rel;
  244. {
  245.    char    pids[60];
  246.    ushort *pid, tpid;
  247.    int     i, j;
  248.  
  249. /*
  250.  * FLOW FOR GETTING   ( example queue:  12  13  14  00  19  22  00  00  00... )
  251.  * INTO THE QUEUE:    (           pos:   0   1   2   3   4   5   6   7   8... )
  252.  *
  253.  *     set hacklock  -- This guarantees that only one process will try to get
  254.  *                      into the queue at once--avoids race conditions.
  255.  *
  256.  * WAIT:
  257.  *     pos = the first zero in the right-hand set of contiguous zeroes
  258.  *           (position 6 in the example above)
  259.  *     look for a queuehole (position 3 above): -- if we were to just set
  260.  *        a lock when the queue has a hole in it, we'd possibly escalate
  261.  *        the length of the queue, whereas if we wait a few seconds, it'll
  262.  *        shrink itself (when process 19 wakes up and moves itself).
  263.  *     if queuehole
  264.  *        check strobe for our blocking process (pos 5, pid 22 in this case)
  265.  *        if strobe hasn't changed and elapsed time > 3 seconds
  266.  *           pos -= 1      -- move over the dead process and erase its hold on
  267.  *           write PID, 0  -- the queue--try again and we'll start here.
  268.  *        else
  269.  *           pause         -- let other processes work without extra I/O
  270.  *        goto WAIT        -- go check the queue for a hole again.
  271.  *     if the queue's full (pos == 30 -- no free slots), return MB_BUSY
  272.  *
  273.  *     clear hacklock      -- we're assured this position in the queue now
  274.  *
  275.  */
  276.  
  277.    if (rel->exc & 2)  baderr (MB_OKAY);
  278.  
  279.    if (_set_hack (rel))   baderr (mb_errno);
  280.  
  281.    _clrstrobe (rel);
  282.  
  283. lockwait:
  284.  
  285.    lseek (rel->lckcode, lckPOS_QUEUE, 0);
  286.    readx (rel->lckcode, pids, 60);
  287.  
  288.    for (i = 29; i >= 0; i--)
  289.       if (*(pid = (ushort *)&pids[i*2]) != 0)
  290.          break;
  291.    i++;           /* "i" now == first available zero. */
  292.  
  293.    if (i != 0)    /* Check for a queuehole before taking the slot. */
  294.       {
  295.       for (j = i-1; j >= 0; j--)
  296.          if (*(pid = (ushort *)&pids[j*2]) == 0)
  297.             break;
  298.  
  299.       if (j != -1)  /* If this != -1, there's a 0 right here in the queue */
  300.          {
  301.          if (! _is_dead (rel, i-1))  /* If it's not dead, we expect it's     */
  302.             {                   /* checking the guy before it, and so on--   */
  303.             _lck_pause ();      /* and that eventually, someone will see the */
  304.             _lck_pause ();      /* queuehole exists and will try to get it   */
  305.             }                   /* filled.                                   */
  306.          else
  307.             {
  308.             i--;       /* If it IS dead, though, move over it and try again. */
  309.             tpid = 0;
  310.             lseek (rel->lckcode, lckPOS_QUEUE +2*i, 0);
  311.             writx (rel->lckcode, &tpid, 2);
  312.             }
  313.          goto lockwait; /* Look, GOTO was useful here, all right?  Sheesh... */
  314.          }
  315.       }
  316.    if (i == 30)
  317.       {
  318.       _clr_hack (rel);
  319.       baderr (MB_BUSY);
  320.       }
  321.  
  322.    lseek (rel->lckcode, lckPOS_QUEUE +2*i, 0);
  323.    writx (rel->lckcode, &rel->pid, 2);
  324.  
  325.    _clr_hack (rel);
  326.  
  327. /*
  328.  * FLOW FOR WORKING OUR    ( example queue:   15  13  12  92  34  16  00... )
  329.  * WAY UP THE QUEUE:       (           pos:    0   1   2   3   4   5   6... )
  330.  *
  331.  * (we're in slot #4, PID==34, in the example above):
  332.  *
  333.  * WAIT:
  334.  *   If we're in slot 0, goto DONE
  335.  *   Otherwise,
  336.  *      Read pos OurPos-1 (#3)--check pid (92)
  337.  *      If PID==0,                     -- The process that WAS there has moved,
  338.  *      OR PID is dead                 -- or hasn't strobed in 3 seconds,
  339.  *         Write our PID in that slot  -- move up over it.  This way, free
  340.  *         Write zero in our last slot -- slots bubble upwards...
  341.  *         Goto WAIT
  342.  *      Strobe our position
  343.  *      Goto WAIT
  344.  *
  345.  * DONE:
  346.  *   We're finished, and a temporary lock is in place.  Make sure to strobe
  347.  *   every second during operations, or you'll lose your lock.
  348.  *
  349.  */
  350.  
  351.    _clrstrobe (rel);
  352.  
  353.    for (j = 0; i > 0; j++)
  354.       {
  355.       lseek (rel->lckcode, lckPOS_QUEUE +2*(i-1), 0);
  356.       readx (rel->lckcode, &tpid, 2);
  357.       if (tpid == 0 || _is_dead (rel, i-1))
  358.          {
  359.          i--;
  360.          tpid = 0;
  361.          lseek (rel->lckcode, lckPOS_QUEUE +2*i, 0);
  362.          writx (rel->lckcode, &rel->pid, 2);
  363.          writx (rel->lckcode, &tpid,     2);
  364.          continue;
  365.          }
  366.  
  367.       _strobe (rel, i);  /* Don't let anyone think we're dead, but let */
  368.       _lck_pause ();     /* other processes think for a second without */
  369.       continue;          /* tons of I/O slowing everything down.       */
  370.       }
  371.  
  372.    rel->exc |= 2;
  373.  
  374.    baderr (MB_OKAY);
  375. }
  376.  
  377. mb_err
  378. _clr_lck (rel)
  379. relation *rel;
  380. {
  381.    ushort tpid = 0;
  382.  
  383.    if (! (rel->exc & 2))  baderr (MB_OKAY);
  384.  
  385.    rel->exc &= 1;  /* Clear the temp lock bit */
  386.  
  387.    lseek (rel->lckcode, lckPOS_QUEUE, 0);
  388.    writx (rel->lckcode, &tpid, 2);
  389.  
  390.    return MB_OKAY;
  391. }
  392.  
  393. static int
  394. _is_dead (rel, pos)
  395. relation *rel;
  396. int            pos;
  397. {
  398.    char    newstrobe[30];   /* Values just read from lockfile         */
  399.    mb_time cur;             /* Current time (reduces curtime() calls) */
  400.    int     i;
  401.  
  402.    cur = curtime();
  403.  
  404. /*
  405.  * If you have lots of frequently-dying processes, you may want to change the
  406.  * thing below to "#if 0"--that way, you can detect ALL processes dying in
  407.  * exactly three seconds instead of three sec per process--does a bit more
  408.  * I/O, though.
  409.  *
  410.  */
  411.  
  412. #if 1
  413.    i = pos;
  414.  
  415.    lseek (rel->lckcode, lckPOS_STROBE +2*i, 0);    /* Get just this one */
  416.    readx (rel->lckcode, &newstrobe[i], 1);         /* position's strobe */
  417. #else
  418.    lseek (rel->lckcode, lckPOS_STROBE, 0);  /* First, we read all thirty */
  419.    readx (rel->lckcode, newstrobe, 30);     /* strobes into an array.    */
  420.  
  421.    for (i = 0; i < 30; i++)                /* For each strobe, check if the  */
  422. #endif
  423.       if (rel->strobe[i] != newstrobe[i])  /* value's changed--if so, update */
  424.          rel->times[i] = cur;              /* times[] array to current time. */
  425.  
  426. /*
  427.  * Note: elap_t() will fail at midnight--it'll return a BIG negative number,
  428.  *       which won't pass the IsItDead test below.  So it may take 6 seconds
  429.  *       to detect if a process is dead, if successive checks occur right then.
  430.  *
  431.  * Now why 10 seconds?
  432.  *    1-second granularity means two seconds are minimum.
  433.  *    Previous value == current value adds two seconds for trigger (strobing
  434.  *       process won't change it, even if it expects to--and won't try again
  435.  *       for 1 sec, plus granularity safeguard).
  436.  *    6-second safeguard (just to be SURE, 'cause it's a Bad Thing to be
  437.  *       trigger happy, and a one-time timeout isn't worth fussing over).
  438.  *
  439.  */
  440.  
  441.    return (elap_t (rel->times[pos]) > 10);  /* If not changed yet, dead. */
  442. }
  443.  
  444. void
  445. _strobe  (rel, pos)
  446. relation *rel;
  447. int            pos;
  448. {
  449.    if (elap_t (rel->times[pos]) >= 1)
  450.       {
  451.       lseek (rel->lckcode, lckPOS_STROBE +pos, 0);
  452.       rel->strobe[pos] = (char)( ((int)rel->strobe[pos] +1) % 255 );
  453.       writx (rel->lckcode, &rel->strobe[pos], 1);
  454.       rel->times[pos] = curtime();
  455.       }
  456. }
  457.  
  458. static void
  459. _clrstrobe (rel)
  460. relation   *rel;
  461. {
  462.    int     i;
  463.    mb_time cur;
  464.    cur = curtime();
  465.    for (i = 0; i < 30; i++)
  466.       rel->times[i] = curtime();
  467. }
  468.  
  469.