1998-01-16 07:13:05 -05:00
|
|
|
#
|
2011-05-18 20:07:25 -04:00
|
|
|
# thread.rb - thread support classes
|
|
|
|
# by Yukihiro Matsumoto <matz@netlab.co.jp>
|
2000-05-01 05:42:38 -04:00
|
|
|
#
|
2001-05-16 05:05:54 -04:00
|
|
|
# Copyright (C) 2001 Yukihiro Matsumoto
|
2000-05-01 05:42:38 -04:00
|
|
|
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
|
2000-05-09 00:53:16 -04:00
|
|
|
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
|
1998-01-16 07:13:05 -05:00
|
|
|
#
|
|
|
|
|
|
|
|
unless defined? Thread
|
2005-12-29 07:05:16 -05:00
|
|
|
raise "Thread not available for this ruby interpreter"
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
unless defined? ThreadError
|
2006-12-31 10:02:22 -05:00
|
|
|
class ThreadError < StandardError
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
1999-01-19 23:59:39 -05:00
|
|
|
if $DEBUG
|
|
|
|
Thread.abort_on_exception = true
|
|
|
|
end
|
|
|
|
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# ConditionVariable objects augment class Mutex. Using condition variables,
|
2003-01-17 04:30:11 -05:00
|
|
|
# it is possible to suspend while in the middle of a critical section until a
|
2005-06-07 05:41:17 -04:00
|
|
|
# resource becomes available.
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Example:
|
|
|
|
#
|
|
|
|
# require 'thread'
|
|
|
|
#
|
|
|
|
# mutex = Mutex.new
|
|
|
|
# resource = ConditionVariable.new
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2003-01-17 04:30:11 -05:00
|
|
|
# a = Thread.new {
|
|
|
|
# mutex.synchronize {
|
|
|
|
# # Thread 'a' now needs the resource
|
|
|
|
# resource.wait(mutex)
|
|
|
|
# # 'a' can now have the resource
|
|
|
|
# }
|
|
|
|
# }
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2003-01-17 04:30:11 -05:00
|
|
|
# b = Thread.new {
|
|
|
|
# mutex.synchronize {
|
|
|
|
# # Thread 'b' has finished using the resource
|
|
|
|
# resource.signal
|
|
|
|
# }
|
|
|
|
# }
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
class ConditionVariable
|
2005-06-07 05:41:17 -04:00
|
|
|
#
|
|
|
|
# Creates a new ConditionVariable
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def initialize
|
|
|
|
@waiters = []
|
2007-02-24 02:01:18 -05:00
|
|
|
@waiters_mutex = Mutex.new
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
|
|
|
|
#
|
2009-09-23 10:01:07 -04:00
|
|
|
# If +timeout+ is given, this method returns after +timeout+ seconds passed,
|
|
|
|
# even if no other thread doesn't signal.
|
|
|
|
#
|
|
|
|
def wait(mutex, timeout=nil)
|
2012-11-30 12:39:36 -05:00
|
|
|
Thread.async_interrupt_timing(StandardError => :defer) do
|
|
|
|
begin
|
|
|
|
Thread.async_interrupt_timing(StandardError => :on_blocking) do
|
|
|
|
@waiters_mutex.synchronize do
|
|
|
|
@waiters.push(Thread.current)
|
|
|
|
end
|
|
|
|
mutex.sleep timeout
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
@waiters_mutex.synchronize do
|
|
|
|
@waiters.delete(Thread.current)
|
|
|
|
end
|
2010-05-09 13:10:18 -04:00
|
|
|
end
|
1999-08-13 01:45:20 -04:00
|
|
|
end
|
2010-01-27 09:30:37 -05:00
|
|
|
self
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Wakes up the first thread in line waiting for this lock.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def signal
|
2012-11-30 12:39:36 -05:00
|
|
|
Thread.async_interrupt_timing(RuntimeError => :on_blocking) do
|
|
|
|
begin
|
|
|
|
t = @waiters_mutex.synchronize {@waiters.shift}
|
|
|
|
t.run if t
|
|
|
|
rescue ThreadError
|
|
|
|
retry # t was alread dead?
|
|
|
|
end
|
2000-03-17 03:58:21 -05:00
|
|
|
end
|
2010-01-27 09:30:37 -05:00
|
|
|
self
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Wakes up all threads waiting for this lock.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def broadcast
|
2012-11-30 12:39:36 -05:00
|
|
|
Thread.async_interrupt_timing(RuntimeError => :on_blocking) do
|
|
|
|
waiters0 = nil
|
|
|
|
@waiters_mutex.synchronize do
|
|
|
|
waiters0 = @waiters.dup
|
|
|
|
@waiters.clear
|
|
|
|
end
|
|
|
|
for t in waiters0
|
|
|
|
begin
|
|
|
|
t.run
|
|
|
|
rescue ThreadError
|
|
|
|
end
|
2000-03-17 03:58:21 -05:00
|
|
|
end
|
1999-08-13 01:45:20 -04:00
|
|
|
end
|
2010-01-27 09:30:37 -05:00
|
|
|
self
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# This class provides a way to synchronize communication between threads.
|
|
|
|
#
|
|
|
|
# Example:
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# require 'thread'
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# queue = Queue.new
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# producer = Thread.new do
|
|
|
|
# 5.times do |i|
|
|
|
|
# sleep rand(i) # simulate expense
|
|
|
|
# queue << i
|
|
|
|
# puts "#{i} produced"
|
|
|
|
# end
|
|
|
|
# end
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# consumer = Thread.new do
|
|
|
|
# 5.times do |i|
|
|
|
|
# value = queue.pop
|
|
|
|
# sleep rand(i/2) # simulate expense
|
|
|
|
# puts "consumed #{value}"
|
|
|
|
# end
|
|
|
|
# end
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# consumer.join
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
1998-01-16 07:13:05 -05:00
|
|
|
class Queue
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Creates a new queue.
|
|
|
|
#
|
1998-01-16 07:13:05 -05:00
|
|
|
def initialize
|
|
|
|
@que = []
|
2011-08-26 18:22:37 -04:00
|
|
|
@que.taint # enable tainted communication
|
2012-11-30 13:55:09 -05:00
|
|
|
@num_waiting = 0
|
1999-01-19 23:59:39 -05:00
|
|
|
self.taint
|
2006-12-31 10:02:22 -05:00
|
|
|
@mutex = Mutex.new
|
2012-11-30 13:55:09 -05:00
|
|
|
@cond = ConditionVariable.new
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Pushes +obj+ to the queue.
|
|
|
|
#
|
1998-01-16 07:13:05 -05:00
|
|
|
def push(obj)
|
2012-11-30 13:55:09 -05:00
|
|
|
Thread.async_interrupt_timing(StandardError => :on_blocking) do
|
|
|
|
@mutex.synchronize do
|
|
|
|
@que.push obj
|
|
|
|
@cond.signal
|
2006-12-31 10:02:22 -05:00
|
|
|
end
|
2012-11-30 13:55:09 -05:00
|
|
|
end
|
2000-05-30 00:24:17 -04:00
|
|
|
end
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of push
|
|
|
|
#
|
2001-05-16 05:05:54 -04:00
|
|
|
alias << push
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of push
|
|
|
|
#
|
2001-05-16 05:05:54 -04:00
|
|
|
alias enq push
|
1998-01-16 07:13:05 -05:00
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Retrieves data from the queue. If the queue is empty, the calling thread is
|
|
|
|
# suspended until data is pushed onto the queue. If +non_block+ is true, the
|
|
|
|
# thread isn't suspended, and an exception is raised.
|
|
|
|
#
|
2000-05-30 00:24:17 -04:00
|
|
|
def pop(non_block=false)
|
2012-11-30 13:55:09 -05:00
|
|
|
Thread.async_interrupt_timing(StandardError => :on_blocking) do
|
|
|
|
@mutex.synchronize do
|
2012-09-09 08:32:33 -04:00
|
|
|
while true
|
|
|
|
if @que.empty?
|
2012-11-30 13:55:09 -05:00
|
|
|
if non_block
|
|
|
|
raise ThreadError, "queue empty"
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
@num_waiting += 1
|
|
|
|
@cond.wait @mutex
|
|
|
|
ensure
|
|
|
|
@num_waiting -= 1
|
|
|
|
end
|
|
|
|
end
|
2012-09-09 08:32:33 -04:00
|
|
|
else
|
|
|
|
return @que.shift
|
|
|
|
end
|
2006-12-31 10:02:22 -05:00
|
|
|
end
|
2010-04-16 07:10:08 -04:00
|
|
|
end
|
2012-11-30 13:55:09 -05:00
|
|
|
end
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of pop
|
|
|
|
#
|
2001-05-16 05:05:54 -04:00
|
|
|
alias shift pop
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of pop
|
|
|
|
#
|
2001-05-16 05:05:54 -04:00
|
|
|
alias deq pop
|
1998-01-16 07:13:05 -05:00
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
2006-12-31 10:02:22 -05:00
|
|
|
# Returns +true+ if the queue is empty.
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
1998-01-16 07:13:05 -05:00
|
|
|
def empty?
|
2001-05-16 05:05:54 -04:00
|
|
|
@que.empty?
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Removes all objects from the queue.
|
|
|
|
#
|
1999-08-13 01:45:20 -04:00
|
|
|
def clear
|
2001-05-16 05:05:54 -04:00
|
|
|
@que.clear
|
1999-08-13 01:45:20 -04:00
|
|
|
end
|
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Returns the length of the queue.
|
|
|
|
#
|
1998-01-16 07:13:05 -05:00
|
|
|
def length
|
|
|
|
@que.length
|
|
|
|
end
|
2003-01-17 04:30:11 -05:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of length.
|
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
alias size length
|
1999-01-19 23:59:39 -05:00
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Returns the number of threads waiting on the queue.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def num_waiting
|
2012-11-30 13:55:09 -05:00
|
|
|
@num_waiting
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# This class represents queues of specified size capacity. The push operation
|
2003-01-17 04:30:11 -05:00
|
|
|
# may be blocked if the capacity is full.
|
|
|
|
#
|
2005-06-07 05:41:17 -04:00
|
|
|
# See Queue for an example of how a SizedQueue works.
|
|
|
|
#
|
2006-12-31 10:02:22 -05:00
|
|
|
class SizedQueue < Queue
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Creates a fixed-length queue with a maximum size of +max+.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def initialize(max)
|
2002-05-29 01:20:39 -04:00
|
|
|
raise ArgumentError, "queue size must be positive" unless max > 0
|
1999-01-19 23:59:39 -05:00
|
|
|
@max = max
|
2012-11-30 13:55:09 -05:00
|
|
|
@enque_cond = ConditionVariable.new
|
|
|
|
@num_enqueue_waiting = 0
|
1999-01-19 23:59:39 -05:00
|
|
|
super()
|
|
|
|
end
|
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Returns the maximum size of the queue.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def max
|
|
|
|
@max
|
|
|
|
end
|
|
|
|
|
2003-01-17 04:30:11 -05:00
|
|
|
#
|
|
|
|
# Sets the maximum size of the queue.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def max=(max)
|
2011-09-03 05:46:51 -04:00
|
|
|
raise ArgumentError, "queue size must be positive" unless max > 0
|
2012-11-30 13:55:09 -05:00
|
|
|
|
|
|
|
@mutex.synchronize do
|
2006-12-31 10:02:22 -05:00
|
|
|
if max <= @max
|
|
|
|
@max = max
|
|
|
|
else
|
|
|
|
diff = max - @max
|
|
|
|
@max = max
|
2012-11-30 13:55:09 -05:00
|
|
|
diff.times do
|
|
|
|
@enque_cond.signal
|
2011-05-18 17:19:18 -04:00
|
|
|
end
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
max
|
|
|
|
end
|
|
|
|
|
2005-06-07 05:41:17 -04:00
|
|
|
#
|
|
|
|
# Pushes +obj+ to the queue. If there is no space left in the queue, waits
|
|
|
|
# until space becomes available.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def push(obj)
|
2012-11-30 13:55:09 -05:00
|
|
|
Thread.async_interrupt_timing(RuntimeError => :on_blocking) do
|
|
|
|
@mutex.synchronize do
|
2012-09-09 08:32:33 -04:00
|
|
|
while true
|
|
|
|
break if @que.length < @max
|
2012-11-30 13:55:09 -05:00
|
|
|
@num_enqueue_waiting += 1
|
|
|
|
begin
|
|
|
|
@enque_cond.wait @mutex
|
|
|
|
ensure
|
|
|
|
@num_enqueue_waiting -= 1
|
|
|
|
end
|
2012-09-09 08:32:33 -04:00
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2012-11-30 13:55:09 -05:00
|
|
|
@que.push obj
|
|
|
|
@cond.signal
|
2006-12-31 10:02:22 -05:00
|
|
|
end
|
2012-11-30 13:55:09 -05:00
|
|
|
end
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of push
|
|
|
|
#
|
2001-05-16 05:05:54 -04:00
|
|
|
alias << push
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of push
|
|
|
|
#
|
2002-08-27 04:31:08 -04:00
|
|
|
alias enq push
|
1999-01-19 23:59:39 -05:00
|
|
|
|
2005-06-07 05:41:17 -04:00
|
|
|
#
|
|
|
|
# Retrieves data from the queue and runs a waiting thread, if any.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def pop(*args)
|
2001-05-16 05:05:54 -04:00
|
|
|
retval = super
|
2012-11-30 13:55:09 -05:00
|
|
|
@mutex.synchronize do
|
2006-12-31 10:02:22 -05:00
|
|
|
if @que.length < @max
|
2012-11-30 13:55:09 -05:00
|
|
|
@enque_cond.signal
|
2001-05-16 05:05:54 -04:00
|
|
|
end
|
2012-11-30 13:55:09 -05:00
|
|
|
end
|
2001-05-16 05:05:54 -04:00
|
|
|
retval
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of pop
|
|
|
|
#
|
2002-08-27 04:31:08 -04:00
|
|
|
alias shift pop
|
2005-06-07 05:41:17 -04:00
|
|
|
|
|
|
|
#
|
|
|
|
# Alias of pop
|
|
|
|
#
|
2002-08-27 04:31:08 -04:00
|
|
|
alias deq pop
|
1999-01-19 23:59:39 -05:00
|
|
|
|
2005-06-07 05:41:17 -04:00
|
|
|
#
|
|
|
|
# Returns the number of threads waiting on the queue.
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
def num_waiting
|
2012-11-30 13:55:09 -05:00
|
|
|
@num_waiting + @num_enqueue_waiting
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
2003-01-17 04:30:11 -05:00
|
|
|
|
|
|
|
# Documentation comments:
|
|
|
|
# - How do you make RDoc inherit documentation from superclass?
|