diff options
Diffstat (limited to 'lib/monitor.rb')
| -rw-r--r-- | lib/monitor.rb | 188 |
1 files changed, 52 insertions, 136 deletions
diff --git a/lib/monitor.rb b/lib/monitor.rb index 07394b5900..21329a5de7 100644 --- a/lib/monitor.rb +++ b/lib/monitor.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: false # = monitor.rb # # Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org> @@ -5,20 +6,17 @@ # This library is distributed under the terms of the Ruby license. # You can freely distribute/modify this library. # - -require 'thread' - # # In concurrent programming, a monitor is an object or module intended to be -# used safely by more than one thread. The defining characteristic of a -# monitor is that its methods are executed with mutual exclusion. That is, at +# used safely by more than one thread. The defining characteristic of a +# monitor is that its methods are executed with mutual exclusion. That is, at # each point in time, at most one thread may be executing any of its methods. # This mutual exclusion greatly simplifies reasoning about the implementation # of monitors compared to reasoning about parallel code that updates a data # structure. # # You can read more about the general principles on the Wikipedia page for -# Monitors[http://en.wikipedia.org/wiki/Monitor_%28synchronization%29] +# Monitors[https://en.wikipedia.org/wiki/Monitor_%28synchronization%29]. # # == Examples # @@ -49,7 +47,7 @@ require 'thread' # end # # The consumer thread waits for the producer thread to push a line to buf -# while <tt>buf.empty?</tt>. The producer thread (main thread) reads a +# while <tt>buf.empty?</tt>. The producer thread (main thread) reads a # line from ARGF and pushes it into buf then calls <tt>empty_cond.signal</tt> # to notify the consumer thread of new data. # @@ -88,75 +86,16 @@ require 'thread' # MonitorMixin module. # module MonitorMixin + ConditionVariable = Monitor::ConditionVariable # :nodoc: + # # FIXME: This isn't documented in Nutshell. # # Since MonitorMixin.new_cond returns a ConditionVariable, and the example # above calls while_wait and signal, this class should be documented. # - class ConditionVariable - class Timeout < Exception; end - - # - # Releases the lock held in the associated monitor and waits; reacquires the lock on wakeup. - # - # If +timeout+ is given, this method returns after +timeout+ seconds passed, - # even if no other thread doesn't signal. - # - def wait(timeout = nil) - @monitor.__send__(:mon_check_owner) - count = @monitor.__send__(:mon_exit_for_cond) - begin - @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout) - return true - ensure - @monitor.__send__(:mon_enter_for_cond, count) - end - end - - # - # Calls wait repeatedly while the given block yields a truthy value. - # - def wait_while - while yield - wait - end - end - - # - # Calls wait repeatedly until the given block yields a truthy value. - # - def wait_until - until yield - wait - end - end - # - # Wakes up the first thread in line waiting for this lock. - # - def signal - @monitor.__send__(:mon_check_owner) - @cond.signal - end - - # - # Wakes up all threads waiting for this lock. - # - def broadcast - @monitor.__send__(:mon_check_owner) - @cond.broadcast - end - - private - - def initialize(monitor) - @monitor = monitor - @cond = ::ConditionVariable.new - end - end - - def self.extend_object(obj) + def self.extend_object(obj) # :nodoc: super(obj) obj.__send__(:mon_initialize) end @@ -165,14 +104,7 @@ module MonitorMixin # Attempts to enter exclusive section. Returns +false+ if lock fails. # def mon_try_enter - if @mon_owner != Thread.current - unless @mon_mutex.try_lock - return false - end - @mon_owner = Thread.current - end - @mon_count += 1 - return true + @mon_data.try_enter end # For backward compatibility alias try_mon_enter mon_try_enter @@ -181,11 +113,7 @@ module MonitorMixin # Enters exclusive section. # def mon_enter - if @mon_owner != Thread.current - @mon_mutex.lock - @mon_owner = Thread.current - end - @mon_count += 1 + @mon_data.enter end # @@ -193,11 +121,21 @@ module MonitorMixin # def mon_exit mon_check_owner - @mon_count -=1 - if @mon_count == 0 - @mon_owner = nil - @mon_mutex.unlock - end + @mon_data.exit + end + + # + # Returns true if this monitor is locked by any thread + # + def mon_locked? + @mon_data.mon_locked? + end + + # + # Returns true if this monitor is locked by current thread. + # + def mon_owned? + @mon_data.mon_owned? end # @@ -205,22 +143,21 @@ module MonitorMixin # section automatically when the block exits. See example under # +MonitorMixin+. # - def mon_synchronize - mon_enter - begin - yield - ensure - mon_exit - end + def mon_synchronize(&b) + @mon_data.synchronize(&b) end alias synchronize mon_synchronize # # Creates a new MonitorMixin::ConditionVariable associated with the - # receiver. + # Monitor object. # def new_cond - return ConditionVariable.new(self) + unless defined?(@mon_data) + mon_initialize + @mon_initialized_by_new_cond = true + end + return ConditionVariable.new(@mon_data) end private @@ -228,7 +165,7 @@ module MonitorMixin # Use <tt>extend MonitorMixin</tt> or <tt>include MonitorMixin</tt> instead # of this constructor. Have look at the examples above to understand how to # use this module. - def initialize(*args) + def initialize(...) super mon_initialize end @@ -236,48 +173,32 @@ module MonitorMixin # Initializes the MonitorMixin after being included in a class or when an # object has been extended with the MonitorMixin def mon_initialize - @mon_owner = nil - @mon_count = 0 - @mon_mutex = Mutex.new - end - - def mon_check_owner - if @mon_owner != Thread.current - raise ThreadError, "current thread not owner" + if defined?(@mon_data) + if defined?(@mon_initialized_by_new_cond) + return # already initialized. + elsif @mon_data_owner_object_id == self.object_id + raise ThreadError, "already initialized" + end end + @mon_data = ::Monitor.new + @mon_data_owner_object_id = self.object_id end - def mon_enter_for_cond(count) - @mon_owner = Thread.current - @mon_count = count - end - - def mon_exit_for_cond - count = @mon_count - @mon_owner = nil - @mon_count = 0 - return count + # Ensures that the MonitorMixin is owned by the current thread, + # otherwise raises an exception. + def mon_check_owner + @mon_data.mon_check_owner end end -# Use the Monitor class when you want to have a lock object for blocks with -# mutual exclusion. -# -# require 'monitor' -# -# lock = Monitor.new -# lock.synchronize do -# # exclusive access -# end -# -class Monitor - include MonitorMixin - alias try_enter try_mon_enter - alias enter mon_enter - alias exit mon_exit +class Monitor # :nodoc: + alias try_mon_enter try_enter + alias mon_try_enter try_enter + alias mon_enter enter + alias mon_exit exit + alias mon_synchronize synchronize end - # Documentation comments: # - All documentation comes from Nutshell. # - MonitorMixin.new_cond appears in the example, but is not documented in @@ -293,8 +214,3 @@ end # directly in the RDoc output. # - in short, it may be worth changing the code layout in this file to make the # documentation easier - -# Local variables: -# mode: Ruby -# tab-width: 8 -# End: |
