home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / ruby / 1.8 / thread.rb < prev    next >
Encoding:
Ruby Source  |  2005-06-07  |  8.2 KB  |  485 lines

  1. #
  2. #        thread.rb - thread support classes
  3. #            $Date: 2005/06/07 09:41:17 $
  4. #            by Yukihiro Matsumoto <matz@netlab.co.jp>
  5. #
  6. # Copyright (C) 2001  Yukihiro Matsumoto
  7. # Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
  8. # Copyright (C) 2000  Information-technology Promotion Agency, Japan
  9. #
  10.  
  11. unless defined? Thread
  12.   fail "Thread not available for this ruby interpreter"
  13. end
  14.  
  15. unless defined? ThreadError
  16.   class ThreadError<StandardError
  17.   end
  18. end
  19.  
  20. if $DEBUG
  21.   Thread.abort_on_exception = true
  22. end
  23.  
  24. class Thread
  25.   #
  26.   # Wraps a block in Thread.critical, restoring the original value upon exit
  27.   # from the critical section.
  28.   #
  29.   def Thread.exclusive
  30.     _old = Thread.critical
  31.     begin
  32.       Thread.critical = true
  33.       return yield
  34.     ensure
  35.       Thread.critical = _old
  36.     end
  37.   end
  38. end
  39.  
  40. #
  41. # Mutex implements a simple semaphore that can be used to coordinate access to
  42. # shared data from multiple concurrent threads.
  43. #
  44. # Example:
  45. #
  46. #   require 'thread'
  47. #   semaphore = Mutex.new
  48. #   
  49. #   a = Thread.new {
  50. #     semaphore.synchronize {
  51. #       # access shared resource
  52. #     }
  53. #   }
  54. #   
  55. #   b = Thread.new {
  56. #     semaphore.synchronize {
  57. #       # access shared resource
  58. #     }
  59. #   }
  60. #
  61. class Mutex
  62.   #
  63.   # Creates a new Mutex
  64.   #
  65.   def initialize
  66.     @waiting = []
  67.     @locked = false;
  68.     @waiting.taint        # enable tainted comunication
  69.     self.taint
  70.   end
  71.  
  72.   #
  73.   # Returns +true+ if this lock is currently held by some thread.
  74.   #
  75.   def locked?
  76.     @locked
  77.   end
  78.  
  79.   #
  80.   # Attempts to obtain the lock and returns immediately. Returns +true+ if the
  81.   # lock was granted.
  82.   #
  83.   def try_lock
  84.     result = false
  85.     Thread.critical = true
  86.     unless @locked
  87.       @locked = true
  88.       result = true
  89.     end
  90.     Thread.critical = false
  91.     result
  92.   end
  93.  
  94.   #
  95.   # Attempts to grab the lock and waits if it isn't available.
  96.   #
  97.   def lock
  98.     while (Thread.critical = true; @locked)
  99.       @waiting.push Thread.current
  100.       Thread.stop
  101.     end
  102.     @locked = true
  103.     Thread.critical = false
  104.     self
  105.   end
  106.  
  107.   #
  108.   # Releases the lock. Returns +nil+ if ref wasn't locked.
  109.   #
  110.   def unlock
  111.     return unless @locked
  112.     Thread.critical = true
  113.     @locked = false
  114.     begin
  115.       t = @waiting.shift
  116.       t.wakeup if t
  117.     rescue ThreadError
  118.       retry
  119.     end
  120.     Thread.critical = false
  121.     begin
  122.       t.run if t
  123.     rescue ThreadError
  124.     end
  125.     self
  126.   end
  127.  
  128.   #
  129.   # Obtains a lock, runs the block, and releases the lock when the block
  130.   # completes.  See the example under Mutex.
  131.   #
  132.   def synchronize
  133.     lock
  134.     begin
  135.       yield
  136.     ensure
  137.       unlock
  138.     end
  139.   end
  140.  
  141.   #
  142.   # If the mutex is locked, unlocks the mutex, wakes one waiting thread, and
  143.   # yields in a critical section.
  144.   #
  145.   def exclusive_unlock
  146.     return unless @locked
  147.     Thread.exclusive do
  148.       @locked = false
  149.       begin
  150.     t = @waiting.shift
  151.     t.wakeup if t
  152.       rescue ThreadError
  153.     retry
  154.       end
  155.       yield
  156.     end
  157.     self
  158.   end
  159. end
  160.  
  161. # ConditionVariable objects augment class Mutex. Using condition variables,
  162. # it is possible to suspend while in the middle of a critical section until a
  163. # resource becomes available.
  164. #
  165. # Example:
  166. #
  167. #   require 'thread'
  168. #
  169. #   mutex = Mutex.new
  170. #   resource = ConditionVariable.new
  171. #   
  172. #   a = Thread.new {
  173. #     mutex.synchronize {
  174. #       # Thread 'a' now needs the resource
  175. #       resource.wait(mutex)
  176. #       # 'a' can now have the resource
  177. #     }
  178. #   }
  179. #   
  180. #   b = Thread.new {
  181. #     mutex.synchronize {
  182. #       # Thread 'b' has finished using the resource
  183. #       resource.signal
  184. #     }
  185. #   }
  186. #
  187. class ConditionVariable
  188.   #
  189.   # Creates a new ConditionVariable
  190.   #
  191.   def initialize
  192.     @waiters = []
  193.   end
  194.   
  195.   #
  196.   # Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
  197.   #
  198.   def wait(mutex)
  199.     begin
  200.       mutex.exclusive_unlock do
  201.         @waiters.push(Thread.current)
  202.         Thread.stop
  203.       end
  204.     ensure
  205.       mutex.lock
  206.     end
  207.   end
  208.   
  209.   #
  210.   # Wakes up the first thread in line waiting for this lock.
  211.   #
  212.   def signal
  213.     begin
  214.       t = @waiters.shift
  215.       t.run if t
  216.     rescue ThreadError
  217.       retry
  218.     end
  219.   end
  220.     
  221.   #
  222.   # Wakes up all threads waiting for this lock.
  223.   #
  224.   def broadcast
  225.     waiters0 = nil
  226.     Thread.exclusive do
  227.       waiters0 = @waiters.dup
  228.       @waiters.clear
  229.     end
  230.     for t in waiters0
  231.       begin
  232.     t.run
  233.       rescue ThreadError
  234.       end
  235.     end
  236.   end
  237. end
  238.  
  239. #
  240. # This class provides a way to synchronize communication between threads.
  241. #
  242. # Example:
  243. #
  244. #   require 'thread'
  245. #   
  246. #   queue = Queue.new
  247. #   
  248. #   producer = Thread.new do
  249. #     5.times do |i|
  250. #       sleep rand(i) # simulate expense
  251. #       queue << i
  252. #       puts "#{i} produced"
  253. #     end
  254. #   end
  255. #   
  256. #   consumer = Thread.new do
  257. #     5.times do |i|
  258. #       value = queue.pop
  259. #       sleep rand(i/2) # simulate expense
  260. #       puts "consumed #{value}"
  261. #     end
  262. #   end
  263. #   
  264. #   consumer.join
  265. #
  266. class Queue
  267.   #
  268.   # Creates a new queue.
  269.   #
  270.   def initialize
  271.     @que = []
  272.     @waiting = []
  273.     @que.taint        # enable tainted comunication
  274.     @waiting.taint
  275.     self.taint
  276.   end
  277.  
  278.   #
  279.   # Pushes +obj+ to the queue.
  280.   #
  281.   def push(obj)
  282.     Thread.critical = true
  283.     @que.push obj
  284.     begin
  285.       t = @waiting.shift
  286.       t.wakeup if t
  287.     rescue ThreadError
  288.       retry
  289.     ensure
  290.       Thread.critical = false
  291.     end
  292.     begin
  293.       t.run if t
  294.     rescue ThreadError
  295.     end
  296.   end
  297.  
  298.   #
  299.   # Alias of push
  300.   #
  301.   alias << push
  302.  
  303.   #
  304.   # Alias of push
  305.   #
  306.   alias enq push
  307.  
  308.   #
  309.   # Retrieves data from the queue.  If the queue is empty, the calling thread is
  310.   # suspended until data is pushed onto the queue.  If +non_block+ is true, the
  311.   # thread isn't suspended, and an exception is raised.
  312.   #
  313.   def pop(non_block=false)
  314.     while (Thread.critical = true; @que.empty?)
  315.       raise ThreadError, "queue empty" if non_block
  316.       @waiting.push Thread.current
  317.       Thread.stop
  318.     end
  319.     @que.shift
  320.   ensure
  321.     Thread.critical = false
  322.   end
  323.  
  324.   #
  325.   # Alias of pop
  326.   #
  327.   alias shift pop
  328.  
  329.   #
  330.   # Alias of pop
  331.   #
  332.   alias deq pop
  333.  
  334.   #
  335.   # Returns +true+ is the queue is empty.
  336.   #
  337.   def empty?
  338.     @que.empty?
  339.   end
  340.  
  341.   #
  342.   # Removes all objects from the queue.
  343.   #
  344.   def clear
  345.     @que.clear
  346.   end
  347.  
  348.   #
  349.   # Returns the length of the queue.
  350.   #
  351.   def length
  352.     @que.length
  353.   end
  354.  
  355.   #
  356.   # Alias of length.
  357.   #
  358.   alias size length
  359.  
  360.   #
  361.   # Returns the number of threads waiting on the queue.
  362.   #
  363.   def num_waiting
  364.     @waiting.size
  365.   end
  366. end
  367.  
  368. #
  369. # This class represents queues of specified size capacity.  The push operation
  370. # may be blocked if the capacity is full.
  371. #
  372. # See Queue for an example of how a SizedQueue works.
  373. #
  374. class SizedQueue<Queue
  375.   #
  376.   # Creates a fixed-length queue with a maximum size of +max+.
  377.   #
  378.   def initialize(max)
  379.     raise ArgumentError, "queue size must be positive" unless max > 0
  380.     @max = max
  381.     @queue_wait = []
  382.     @queue_wait.taint        # enable tainted comunication
  383.     super()
  384.   end
  385.  
  386.   #
  387.   # Returns the maximum size of the queue.
  388.   #
  389.   def max
  390.     @max
  391.   end
  392.  
  393.   #
  394.   # Sets the maximum size of the queue.
  395.   #
  396.   def max=(max)
  397.     Thread.critical = true
  398.     if max <= @max
  399.       @max = max
  400.       Thread.critical = false
  401.     else
  402.       diff = max - @max
  403.       @max = max
  404.       Thread.critical = false
  405.       diff.times do
  406.     begin
  407.       t = @queue_wait.shift
  408.       t.run if t
  409.     rescue ThreadError
  410.       retry
  411.     end
  412.       end
  413.     end
  414.     max
  415.   end
  416.  
  417.   #
  418.   # Pushes +obj+ to the queue.  If there is no space left in the queue, waits
  419.   # until space becomes available.
  420.   #
  421.   def push(obj)
  422.     Thread.critical = true
  423.     while @que.length >= @max
  424.       @queue_wait.push Thread.current
  425.       Thread.stop
  426.       Thread.critical = true
  427.     end
  428.     super
  429.   end
  430.  
  431.   #
  432.   # Alias of push
  433.   #
  434.   alias << push
  435.  
  436.   #
  437.   # Alias of push
  438.   #
  439.   alias enq push
  440.  
  441.   #
  442.   # Retrieves data from the queue and runs a waiting thread, if any.
  443.   #
  444.   def pop(*args)
  445.     retval = super
  446.     Thread.critical = true
  447.     if @que.length < @max
  448.       begin
  449.     t = @queue_wait.shift
  450.     t.wakeup if t
  451.       rescue ThreadError
  452.     retry
  453.       ensure
  454.     Thread.critical = false
  455.       end
  456.       begin
  457.     t.run if t
  458.       rescue ThreadError
  459.       end
  460.     end
  461.     retval
  462.   end
  463.  
  464.   #
  465.   # Alias of pop
  466.   #
  467.   alias shift pop
  468.  
  469.   #
  470.   # Alias of pop
  471.   #
  472.   alias deq pop
  473.  
  474.   #
  475.   # Returns the number of threads waiting on the queue.
  476.   #
  477.   def num_waiting
  478.     @waiting.size + @queue_wait.size
  479.   end
  480. end
  481.  
  482. # Documentation comments:
  483. #  - How do you make RDoc inherit documentation from superclass?
  484.