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