home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / fnb101.zip / Lib / site-packages / Fnorb / orb / condvar.py < prev    next >
Text File  |  1999-06-28  |  10KB  |  360 lines

  1. #############################################################################
  2. # Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1995, 1996, 1997, 1998, 1999
  3. # Unpublished work.  All Rights Reserved.
  4. #
  5. # The software contained on this media is the property of the
  6. # DSTC Pty Ltd.  Use of this software is strictly in accordance
  7. # with the license agreement in the accompanying LICENSE.DOC file.
  8. # If your distribution of this software does not contain a
  9. # LICENSE.DOC file then you have no rights to use this software
  10. # in any manner and should contact DSTC at the address below
  11. # to determine an appropriate licensing arrangement.
  12. #      DSTC Pty Ltd
  13. #      Level 7, GP South
  14. #      Staff House Road
  15. #      University of Queensland
  16. #      St Lucia, 4072
  17. #      Australia
  18. #      Tel: +61 7 3365 4310
  19. #      Fax: +61 7 3365 4311
  20. #      Email: enquiries@dstc.edu.au
  21. # This software is being provided "AS IS" without warranty of
  22. # any kind.  In no event shall DSTC Pty Ltd be liable for
  23. # damage of any kind arising out of or in connection with
  24. # the use or performance of this software.
  25. #
  26. # Project:  Hector
  27. # File:     $Source: /units/arch/src/Fnorb/orb/RCS/condvar.py,v $
  28. #
  29. #############################################################################
  30. """
  31.  
  32. Condition variables
  33.  
  34. """
  35. #############################################################################
  36.  
  37. import signal, thread, time
  38.  
  39.  
  40. #############################################################################
  41.  
  42. class condvar:
  43.     """Condition variables.
  44.  
  45.        A condition variable class with a built-in mutex.  To use,
  46.        create and associate a variable with it.  Others, interested in
  47.        the state of that variable, can "wait()" on the condvar and
  48.        will be suspended, to be resumed whe another thread "signal()"s
  49.        or "broadcast()"s.
  50.  
  51.        You must have acquired the condvar prior to any of "release()",
  52.        "signal()" or "broadcast()".  All of these operations release
  53.        the acquired mutex.
  54.  
  55.        You must also have acquired the condvar prior to "wait()"ing or
  56.        "timedwait()"ing, which release and then reacquire the
  57.        mutex."""
  58.  
  59.  
  60.     #-- timedwait timer table
  61.     d_timer = {}
  62.  
  63.  
  64.     def __init__(self):
  65.     """Create a new condition variable."""
  66.  
  67.     self.lk = thread.allocate_lock()
  68.     self.q_waiting = []
  69.  
  70.  
  71.     def __del__(self):
  72.     """Destroy a condition variable."""
  73.  
  74.     self.lk.acquire()
  75.     
  76.     for thr in self.q_waiting:
  77.         thr.release()
  78.  
  79.  
  80.     def acquire(self):
  81.     """Acquire the lock on a condition variable.
  82.  
  83.     Return value    none
  84.     Pre-Locking     caller must NOT hold the CV lock
  85.     Post-Locking    caller holds the CV lock
  86.  
  87.     Condition variables must be locked before calling "signal()",
  88.     "broadcast()", "wait()", "timedwait()" or "release()"."""
  89.  
  90.     self.lk.acquire()
  91.  
  92.  
  93.     def release(self):
  94.     """Release the currently held lock on a condition variable.
  95.  
  96.     Return value    none
  97.     Pre-Locking     caller must hold the CV lock
  98.     Post-Locking    callers lock is released
  99.  
  100.     The condition variable lock must be currently held by the
  101.     caller."""
  102.  
  103.     self.lk.release()
  104.  
  105.  
  106.     def signal(self):
  107.     """Inform the first waiting thread that the condition is altered.
  108.  
  109.     Return value    none
  110.     Pre-Locking     caller MUST hold the CV lock
  111.     Post-Locking    callers lock is released
  112.  
  113.     The first thread queued awaiting changes in the condition is
  114.     unblocked.  Note that the condition variable lock must be held
  115.     by the caller."""
  116.  
  117.     #-- check for waiting threads
  118.     if len(self.q_waiting) > 0:
  119.  
  120.         #-- remove head of waiting queue
  121.         thr = self.q_waiting[0]
  122.         self.q_waiting = self.q_waiting[1:]
  123.  
  124.         #-- remove timeout record (if present)
  125.         if self.__class__.d_timer.has_key(id(thr)):
  126.         del self.__class__.d_timer[id(thr)]
  127.  
  128.         #-- unblock thread
  129.         thr.release()
  130.  
  131.     #-- unlock CV
  132.     self.lk.release()
  133.  
  134.  
  135.     def broadcast(self):
  136.     """Inform all waiting threads that the condition is altered.
  137.  
  138.     Return value    none
  139.     Pre-Locking     caller MUST hold the CV lock
  140.     Post-Locking    callers lock is released
  141.  
  142.     All threads queued awaiting a change in the condition are
  143.     unblocked.  Note that the condition variable lock must be held
  144.     by the caller."""
  145.  
  146.     for thr in self.q_waiting:
  147.         #-- remove timeout record (if present)
  148.         if self.__class__.d_timer.has_key(id(thr)):
  149.         del self.__class__.d_timer[id(thr)]
  150.  
  151.         #-- unblock thread
  152.         thr.release()
  153.  
  154.     #-- reset waiting queue
  155.     self.q_waiting = []
  156.  
  157.     #-- unlock CV
  158.     self.lk.release()
  159.  
  160.  
  161.     def wait(self):
  162.     """Wait for notification of a change in the condition.
  163.  
  164.     Return value    none
  165.     Pre-Locking     caller MUST hold the CV lock
  166.     Post-Locking    caller holds the CV lock (see note)
  167.  
  168.     The caller is blocked until notified of a change in the
  169.     condition (from either "signal()" or "broadcast()").
  170.  
  171.     Note: the caller must hold the condition variable lock,
  172.     which will be released and reacquired before returning."""
  173.  
  174.     #-- create the blocking lock and add to the CV queue
  175.     lk = thread.allocate_lock()
  176.     lk.acquire()
  177.     self.q_waiting.append(lk)
  178.  
  179.     #-- release the CV lock
  180.     self.lk.release()
  181.  
  182.     #-- block the thread
  183.     lk.acquire()
  184.  
  185.     #-- reacquire the CV lock
  186.     self.lk.acquire()
  187.  
  188.  
  189.     def timedwait(self, timeout = 0):
  190.     """Wait for notification of a change in the condition with a timeout.
  191.  
  192.     "timeout"       period to wait, in seconds.  0 means return 
  193.                     immediately.
  194.     Return value     0 - a change has been notified
  195.                     -1 - the timeout period expired
  196.     Exceptions      ?
  197.     Pre-Locking     caller MUST hold the CV lock
  198.     Post-Locking    caller holds the CV lock (see note)
  199.  
  200.     The caller is blocked until notified of a change in the
  201.     condition (from "signal()" or "broadcast()") or the expiry of
  202.     the timeout period.
  203.  
  204.     Note: the caller must hold the condition variable lock,
  205.     which will be released and reacquired before returning."""
  206.  
  207.     res = 0
  208.  
  209.     #-- create the blocking lock and add to the CV queue
  210.     lk = thread.allocate_lock()
  211.     lk.acquire()
  212.     self.q_waiting.append(lk)
  213.  
  214.     #-- add this thread to the set of timers
  215.     lk_id = id(lk)
  216.     self.__class__.d_timer[lk_id] = lk
  217.     thread.start_new_thread(self._wakeup, (lk_id, timeout))
  218.  
  219.     #-- release the CV lock
  220.     self.lk.release()
  221.  
  222.     #-- block the thread
  223.     lk.acquire()
  224.  
  225.     #-- reacquire the CV lock
  226.     self.lk.acquire()
  227.  
  228.     #-- check to see if we timed out
  229.     if self.__class__.d_timer.has_key(lk_id):
  230.         res = -1
  231.          del self.__class__.d_timer[lk_id]
  232.  
  233.     return res
  234.  
  235.  
  236.     def _wakeup(self, lk_id, timeout):
  237.     """Wakeup a timedwait on a condition variable.
  238.  
  239.     The workings of "timedwait()" are a little obscure: when a
  240.     thread calls "timedwait()" an entry is placed into the timer
  241.     table "d_timer" which is shared by all instances of the class.
  242.     This entry is keyed by the ID of the lock used to suspend the
  243.     thread, and contains the lock instance.
  244.  
  245.     When a thread is woken by "signal()" or "broadcast()" this
  246.     entry is deleted.  Note that it will only be there if
  247.     "timedwait()" has been called.
  248.  
  249.     "timedwait()" checks for the presence of the table entry when
  250.     it is unblocked.  If it is not there, then the thread was
  251.     woken by a "signal()" or "broadcast()" (which will have
  252.     deleted it).
  253.  
  254.     "_wakeup()" unblocks the thread using the lock instance in the
  255.     timer table, but does not delete the entry.  When
  256.     "timedwait()" is unblocked then, it notices that the timer
  257.     table entry is still there, and can determine that it was
  258.     unblocked by the timer expiry, not a "signal()" or
  259.     "broadcast()".
  260.  
  261.     ok?"""
  262.  
  263.     time.sleep(timeout)
  264.  
  265.     #-- check if thread is still blocked
  266.     if self.__class__.d_timer.has_key(lk_id):
  267.         #-- unblock thread
  268.         self.__class__.d_timer[lk_id].release()
  269.  
  270.     thread.exit()
  271.  
  272.  
  273.  
  274. #############################################################################
  275.  
  276. def cvtest():
  277.     """Test function."""
  278.  
  279.     cv = condvar()
  280.     var = 1
  281.  
  282.     print "master: starting threads."
  283.     for id in [1, 2, 3, 4, 5]:
  284.     thread.start_new_thread(tester, (cv, id))
  285.     time.sleep(0)
  286.  
  287.     print
  288.     time.sleep(5)
  289.     print
  290.  
  291.     print "master: about to signal"
  292.     cv.acquire()
  293.     cv.signal()
  294.  
  295.     time.sleep(5)
  296.     print
  297.  
  298.     print "master: about to broadcast"
  299.     cv.acquire()
  300.     cv.broadcast()
  301.  
  302.     time.sleep(5)
  303.     print
  304.     print "master: starting timedwait threads."
  305.     print
  306.  
  307.     thread.start_new_thread(time_tester, (cv, 6, 0))
  308.     thread.start_new_thread(time_tester, (cv, 7, 3))
  309.     thread.start_new_thread(time_tester, (cv, 8, 10))
  310.     thread.start_new_thread(time_tester, (cv, 9, 15))
  311.  
  312.     time.sleep(5)
  313.     cv.acquire()
  314.     print "master: about to broadcast."
  315.     cv.broadcast()
  316.  
  317.     time.sleep(5)
  318.     print
  319.     print "press C-c to exit."
  320.     print
  321.  
  322.     signal.pause()
  323.  
  324.  
  325. def tester(cv, id):
  326.     print "thread %d: Started tester." % (id)
  327.  
  328.     print "thread %d: About to wait." % (id)
  329.     cv.acquire()
  330.     cv.wait()
  331.     cv.release()
  332.     print "thread %d: Released." % (id)
  333.  
  334.     thread.exit()
  335.  
  336.  
  337. def time_tester(cv, id, timeout):
  338.     print "thread %d: Started time tester - waiting %d secs." % (id, timeout)
  339.  
  340.     print "thread %d: About to wait." % (id)
  341.     cv.acquire()
  342.     if cv.timedwait(timeout) == 0:
  343.     print "thread %d: Signalled." % (id)
  344.     else:
  345.     print "thread %d: Timeout expired." % (id)
  346.  
  347.     cv.release()
  348.     thread.exit()
  349.  
  350.  
  351. #############################################################################
  352.  
  353. if __name__ == "__main__":
  354.     cvtest()
  355.  
  356.  
  357. #############################################################################
  358.