summaryrefslogtreecommitdiff
path: root/lib/monitor.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/monitor.rb')
-rw-r--r--lib/monitor.rb188
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: