diff options
author | Koichi Sasada <[email protected]> | 2019-10-20 04:52:20 +0900 |
---|---|---|
committer | GitHub <[email protected]> | 2019-10-20 04:52:20 +0900 |
commit | caac5f777ae288b5982708b8690e712e1cae0cf6 (patch) | |
tree | e4257d65c062b7e8c9e4c4b962cee3ca7c5e1d4c /lib | |
parent | 434966bffddd4299d34f5d1f7f225bf7396d0807 (diff) |
make monitor.so for performance. (#2576)
Recent monitor.rb has performance problem because of interrupt
handlers. 'Monitor#synchronize' is frequently used primitive
so the performance of this method is important.
This patch rewrite 'monitor.rb' with 'monitor.so' (C-extension)
and make it faster. See [Feature #16255] for details.
Monitor class objects are normal object which include MonitorMixin.
This patch introduce a Monitor class which is implemented on C
and MonitorMixin uses Monitor object as re-entrant (recursive)
Mutex. This technique improve performance because we don't need
to care atomicity and we don't need accesses to instance variables
any more on Monitor class.
Notes
Notes:
Merged-By: ko1 <[email protected]>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/monitor.rb | 323 |
1 files changed, 0 insertions, 323 deletions
diff --git a/lib/monitor.rb b/lib/monitor.rb deleted file mode 100644 index c4ce06794a..0000000000 --- a/lib/monitor.rb +++ /dev/null @@ -1,323 +0,0 @@ -# frozen_string_literal: false -# = monitor.rb -# -# Copyright (C) 2001 Shugo Maeda <[email protected]> -# -# This library is distributed under the terms of the Ruby license. -# You can freely distribute/modify this library. -# - -# -# 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 -# 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] -# -# == Examples -# -# === Simple object.extend -# -# require 'monitor.rb' -# -# buf = [] -# buf.extend(MonitorMixin) -# empty_cond = buf.new_cond -# -# # consumer -# Thread.start do -# loop do -# buf.synchronize do -# empty_cond.wait_while { buf.empty? } -# print buf.shift -# end -# end -# end -# -# # producer -# while line = ARGF.gets -# buf.synchronize do -# buf.push(line) -# empty_cond.signal -# end -# 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 -# line from ARGF and pushes it into buf then calls <tt>empty_cond.signal</tt> -# to notify the consumer thread of new data. -# -# === Simple Class include -# -# require 'monitor' -# -# class SynchronizedArray < Array -# -# include MonitorMixin -# -# def initialize(*args) -# super(*args) -# end -# -# alias :old_shift :shift -# alias :old_unshift :unshift -# -# def shift(n=1) -# self.synchronize do -# self.old_shift(n) -# end -# end -# -# def unshift(item) -# self.synchronize do -# self.old_unshift(item) -# end -# end -# -# # other methods ... -# end -# -# +SynchronizedArray+ implements an Array with synchronized access to items. -# This Class is implemented as subclass of Array which includes the -# MonitorMixin module. -# -module MonitorMixin - EXCEPTION_NEVER = {Exception => :never}.freeze - EXCEPTION_IMMEDIATE = {Exception => :immediate}.freeze - - # - # 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) - Thread.handle_interrupt(EXCEPTION_NEVER) do - @monitor.__send__(:mon_check_owner) - count = @monitor.__send__(:mon_exit_for_cond) - begin - Thread.handle_interrupt(EXCEPTION_IMMEDIATE) do - @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout) - end - return true - ensure - @monitor.__send__(:mon_enter_for_cond, count) - end - 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 = Thread::ConditionVariable.new - end - end - - def self.extend_object(obj) - super(obj) - obj.__send__(:mon_initialize) - end - - # - # 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 - @mon_count = 0 - end - @mon_count += 1 - return true - end - # For backward compatibility - alias try_mon_enter mon_try_enter - - # - # Enters exclusive section. - # - def mon_enter - if @mon_owner != Thread.current - @mon_mutex.lock - @mon_owner = Thread.current - @mon_count = 0 - end - @mon_count += 1 - end - - # - # Leaves exclusive section. - # - def mon_exit - mon_check_owner - @mon_count -=1 - if @mon_count == 0 - @mon_owner = nil - @mon_mutex.unlock - end - end - - # - # Returns true if this monitor is locked by any thread - # - def mon_locked? - @mon_mutex.locked? - end - - # - # Returns true if this monitor is locked by current thread. - # - def mon_owned? - @mon_mutex.locked? && @mon_owner == Thread.current - end - - # - # Enters exclusive section and executes the block. Leaves the exclusive - # section automatically when the block exits. See example under - # +MonitorMixin+. - # - def mon_synchronize - # Prevent interrupt on handling interrupts; for example timeout errors - # it may break locking state. - Thread.handle_interrupt(EXCEPTION_NEVER){ mon_enter } - begin - yield - ensure - Thread.handle_interrupt(EXCEPTION_NEVER){ mon_exit } - end - end - alias synchronize mon_synchronize - - # - # Creates a new MonitorMixin::ConditionVariable associated with the - # receiver. - # - def new_cond - return ConditionVariable.new(self) - end - - private - - # 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) - super - mon_initialize - end - - # Initializes the MonitorMixin after being included in a class or when an - # object has been extended with the MonitorMixin - def mon_initialize - if defined?(@mon_mutex) && @mon_mutex_owner_object_id == object_id - raise ThreadError, "already initialized" - end - @mon_mutex = Thread::Mutex.new - @mon_mutex_owner_object_id = object_id - @mon_owner = nil - @mon_count = 0 - end - - def mon_check_owner - if @mon_owner != Thread.current - raise ThreadError, "current thread not owner" - end - 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 - 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 -end - - -# Documentation comments: -# - All documentation comes from Nutshell. -# - MonitorMixin.new_cond appears in the example, but is not documented in -# Nutshell. -# - All the internals (internal modules Accessible and Initializable, class -# ConditionVariable) appear in RDoc. It might be good to hide them, by -# making them private, or marking them :nodoc:, etc. -# - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but -# not synchronize. -# - mon_owner is in Nutshell, but appears as an accessor in a separate module -# here, so is hard/impossible to RDoc. Some other useful accessors -# (mon_count and some queue stuff) are also in this module, and don't appear -# directly in the RDoc output. -# - in short, it may be worth changing the code layout in this file to make the -# documentation easier |