# # 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 # # def enter: () -> nil # # def exit: () -> nil # # def mon_check_owner: () -> nil # # alias mon_enter enter # # alias mon_exit exit # # def mon_locked?: () -> bool # # def mon_owned?: () -> bool # # alias mon_synchronize synchronize # # alias mon_try_enter try_enter # # def new_cond: () -> ::MonitorMixin::ConditionVariable # # def synchronize: [T] () { () -> T } -> T # # def try_enter: () -> bool # # for compatibility # alias try_mon_enter try_enter # # def wait_for_cond: (::MonitorMixin::ConditionVariable, Numeric? timeout) -> untyped end # # 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](https://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 # `buf.empty?`. The producer thread (main thread) reads a line from ARGF and # pushes it into buf then calls `empty_cond.signal` 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 # # def self.extend_object: (untyped obj) -> untyped # # Enters exclusive section. # def mon_enter: () -> nil # # Leaves exclusive section. # def mon_exit: () -> nil # # Returns true if this monitor is locked by any thread # def mon_locked?: () -> bool # # Returns true if this monitor is locked by current thread. # def mon_owned?: () -> bool # # Enters exclusive section and executes the block. Leaves the exclusive section # automatically when the block exits. See example under `MonitorMixin`. # def mon_synchronize: [T] () { () -> T } -> T # # Attempts to enter exclusive section. Returns `false` if lock fails. # def mon_try_enter: () -> bool # # Creates a new MonitorMixin::ConditionVariable associated with the Monitor # object. # def new_cond: () -> ::MonitorMixin::ConditionVariable # # alias synchronize mon_synchronize # # For backward compatibility # alias try_mon_enter mon_try_enter private # # Use `extend MonitorMixin` or `include MonitorMixin` instead of this # constructor. Have look at the examples above to understand how to use this # module. # def initialize: (*untyped) { (*untyped) -> untyped } -> void # # def mon_check_owner: () -> nil # # Initializes the MonitorMixin after being included in a class or when an object # has been extended with the MonitorMixin # def mon_initialize: () -> untyped end # # 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 MonitorMixin::ConditionVariable # # Wakes up all threads waiting for this lock. # def broadcast: () -> Thread::ConditionVariable # # Wakes up the first thread in line waiting for this lock. # def signal: () -> Thread::ConditionVariable # # 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: (?Numeric? timeout) -> untyped # # Calls wait repeatedly until the given block yields a truthy value. # def wait_until: () { () -> boolish } -> untyped # # Calls wait repeatedly while the given block yields a truthy value. # def wait_while: () { () -> boolish } -> untyped private # # def initialize: (Monitor monitor) -> void end