From 6bb525282cdd551bd240557daf019378ee211952 Mon Sep 17 00:00:00 2001 From: matz Date: Mon, 2 Mar 1998 04:03:33 +0000 Subject: *** empty log message *** git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/v1_1r@103 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/monitor.rb | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 lib/monitor.rb (limited to 'lib/monitor.rb') diff --git a/lib/monitor.rb b/lib/monitor.rb new file mode 100644 index 0000000000..81fe8f2b22 --- /dev/null +++ b/lib/monitor.rb @@ -0,0 +1,325 @@ +## monitor.rb + +# Author: Shugo Maeda +# Version: $Revision: 0.1 $ + +# USAGE: +# +# foo = Foo.new +# foo.extend(MonitorMixin) +# cond = foo.new_cond +# +# thread1: +# foo.synchronize { +# ... +# cond.wait_until { foo.done? } +# ... +# } +# +# thread2: +# foo.synchronize { +# foo.do_something +# cond.signal +# } + +# ATTENTION: +# +# If you include MonitorMixin and override `initialize', you should +# call `super'. +# If you include MonitorMixin to built-in classes, you should override +# `new' to call `mon_initialize'. + +## Code: + +require "final" + +module MonitorMixin + + RCS_ID = %q$Id: monitor.rb,v 0.1 1998/03/01 08:40:18 shugo Exp shugo $ + + module Primitive + + include MonitorMixin + + MON_OWNER_TABLE = {} + MON_COUNT_TABLE = {} + MON_ENTERING_QUEUE_TABLE = {} + MON_WAITING_QUEUE_TABLE = {} + + FINALIZER = Proc.new { |id| + MON_OWNER_TABLE.delete(id) + MON_COUNT_TABLE.delete(id) + MON_ENTERING_QUEUE_TABLE.delete(id) + MON_WAITING_QUEUE_TABLE.delete(id) + } + + def self.extend_object(obj) + super(obj) + obj.mon_initialize + end + + def mon_initialize + MON_OWNER_TABLE[id] = nil + MON_COUNT_TABLE[id] = 0 + MON_ENTERING_QUEUE_TABLE[id] = [] + MON_WAITING_QUEUE_TABLE[id] = [] + ObjectSpace.define_finalizer(self, FINALIZER) + end + + def mon_owner + return MON_OWNER_TABLE[id] + end + + def mon_count + return MON_COUNT_TABLE[id] + end + + def mon_entering_queue + return MON_ENTERING_QUEUE_TABLE[id] + end + + def mon_waiting_queue + return MON_WAITING_QUEUE_TABLE[id] + end + + def set_mon_owner(val) + return MON_OWNER_TABLE[id] = val + end + + def set_mon_count(val) + return MON_COUNT_TABLE[id] = val + end + + private :mon_count, :mon_entering_queue, :mon_waiting_queue, + :set_mon_owner, :set_mon_count + end + + module NonPrimitive + + include MonitorMixin + + attr_reader :mon_owner, :mon_count, + :mon_entering_queue, :mon_waiting_queue + + def self.extend_object(obj) + super(obj) + obj.mon_initialize + end + + def mon_initialize + @mon_owner = nil + @mon_count = 0 + @mon_entering_queue = [] + @mon_waiting_queue = [] + end + + def set_mon_owner(val) + @mon_owner = val + end + + def set_mon_count(val) + @mon_count = val + end + + private :mon_count, :mon_entering_queue, :mon_waiting_queue, + :set_mon_owner, :set_mon_count + end + + def self.extendable_module(obj) + if Fixnum === obj or TrueClass === obj or FalseClass === obj or + NilClass === obj + raise TypeError, "MonitorMixin can't extend #{obj.type}" + else + begin + obj.instance_eval("@mon_owner") + return NonPrimitive + rescue TypeError + return Primitive + end + end + end + + def self.extend_object(obj) + obj.extend(extendable_module(obj)) + end + + def self.includable_module(klass) + if klass.instance_of?(Module) + return NonPrimitive + end + begin + dummy = klass.new + return extendable_module(dummy) + rescue ArgumentError + if klass.singleton_methods.include?("new") + return Primitive + else + return NonPrimitive + end + rescue NameError + raise TypeError, "#{klass} can't include MonitorMixin" + end + end + + def self.append_features(klass) + mod = includable_module(klass) + klass.module_eval("include mod") + end + + def initialize(*args) + super + mon_initialize + end + + def try_mon_enter + result = false + Thread.critical = true + if mon_owner.nil? + set_mon_owner(Thread.current) + end + if mon_owner == Thread.current + set_mon_count(mon_count + 1) + result = true + end + Thread.critical = false + return result + end + + def mon_enter + Thread.critical = true + while mon_owner != nil && mon_owner != Thread.current + mon_entering_queue.push(Thread.current) + Thread.stop + Thread.critical = true + end + set_mon_owner(Thread.current) + set_mon_count(mon_count + 1) + Thread.critical = false + end + + def mon_exit + if mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + Thread.critical = true + set_mon_count(mon_count - 1) + if mon_count == 0 + set_mon_owner(nil) + if mon_waiting_queue.empty? + t = mon_entering_queue.shift + else + t = mon_waiting_queue.shift + end + end + t.wakeup if t + Thread.critical = false + Thread.pass + end + + def mon_synchronize + mon_enter + begin + yield + ensure + mon_exit + end + end + alias synchronize mon_synchronize + + class ConditionVariable + def initialize(monitor) + @monitor = monitor + @waiters = [] + end + + def wait + if @monitor.mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + + @monitor.instance_eval(<