diff options
Diffstat (limited to 'lib/puppet/event-loop')
-rw-r--r-- | lib/puppet/event-loop/better-definers.rb | 367 | ||||
-rw-r--r-- | lib/puppet/event-loop/event-loop.rb | 355 | ||||
-rw-r--r-- | lib/puppet/event-loop/signal-system.rb | 220 |
3 files changed, 0 insertions, 942 deletions
diff --git a/lib/puppet/event-loop/better-definers.rb b/lib/puppet/event-loop/better-definers.rb deleted file mode 100644 index 0af37da62..000000000 --- a/lib/puppet/event-loop/better-definers.rb +++ /dev/null @@ -1,367 +0,0 @@ -## better-definers.rb --- better attribute and method definers -# Copyright (C) 2005 Daniel Brockman - -# This program is free software; you can redistribute it -# and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation; -# either version 2 of the License, or (at your option) any -# later version. - -# This file is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty -# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. - -# You should have received a copy of the GNU General Public -# License along with this program; if not, write to the Free -# Software Foundation, 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA. - -class Symbol - def predicate? - to_s.include? "?" end - def imperative? - to_s.include? "!" end - def writer? - to_s.include? "=" end - - def punctuated? - predicate? or imperative? or writer? end - def without_punctuation - to_s.delete("?!=").to_sym end - - def predicate - without_punctuation.to_s + "?" end - def imperative - without_punctuation.to_s + "!" end - def writer - without_punctuation.to_s + "=" end -end - -class Hash - def collect! (&block) - replace Hash[*collect(&block).flatten] - end - - def flatten - to_a.flatten - end -end - -module Kernel - def returning (value) - yield value ; value - end -end - -class Module - def define_hard_aliases (name_pairs) - for new_aliases, existing_name in name_pairs do - new_aliases.kind_of? Array or new_aliases = [new_aliases] - for new_alias in new_aliases do - alias_method(new_alias, existing_name) - end - end - end - - def define_soft_aliases (name_pairs) - for new_aliases, existing_name in name_pairs do - new_aliases.kind_of? Array or new_aliases = [new_aliases] - for new_alias in new_aliases do - class_eval %{def #{new_alias}(*args, &block) - #{existing_name}(*args, &block) end} - end - end - end - - define_soft_aliases \ - :define_hard_alias => :define_hard_aliases, - :define_soft_alias => :define_soft_aliases - - # This method lets you define predicates like :foo?, - # which will be defined to return the value of @foo. - def define_readers (*names) - for name in names.map { |x| x.to_sym } do - if name.punctuated? - # There's no way to define an efficient reader whose - # name is different from the instance variable. - class_eval %{def #{name} ; @#{name.without_punctuation} end} - else - # Use `attr_reader' to define an efficient method. - attr_reader(name) - end - end - end - - def writer_defined? (name) - method_defined? name.to_sym.writer - end - - # If you pass a predicate symbol :foo? to this method, it'll first - # define a regular writer method :foo, without a question mark. - # Then it'll define an imperative writer method :foo! as a shorthand - # for setting the property to true. - def define_writers (*names, &body) - for name in names.map { |x| x.to_sym } do - if block_given? - define_method(name.writer, &body) - else - attr_writer(name.without_punctuation) - end - if name.predicate? - class_eval %{def #{name.imperative} - self.#{name.writer} true end} - end - end - end - - define_soft_aliases \ - :define_reader => :define_readers, - :define_writer => :define_writers - - # We don't need a singular alias for `define_accessors', - # because it always defines at least two methods. - - def define_accessors (*names) - define_readers(*names) - define_writers(*names) - end - - def define_opposite_readers (name_pairs) - name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] } - for opposite_name, name in name_pairs do - define_reader(name) unless method_defined? name - class_eval %{def #{opposite_name} ; not #{name} end} - end - end - - def define_opposite_writers (name_pairs) - name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] } - for opposite_name, name in name_pairs do - define_writer(name) unless writer_defined? name - class_eval %{def #{opposite_name.writer} x - self.#{name.writer} !x end} - class_eval %{def #{opposite_name.imperative} - self.#{name.writer} false end} - end - end - - define_soft_aliases \ - :define_opposite_reader => :define_opposite_readers, - :define_opposite_writer => :define_opposite_writers - - def define_opposite_accessors (name_pairs) - define_opposite_readers name_pairs - define_opposite_writers name_pairs - end - - def define_reader_with_opposite (name_pair, &body) - name, opposite_name = name_pair.flatten.collect { |x| x.to_sym } - define_method(name, &body) - define_opposite_reader(opposite_name => name) - end - - def define_writer_with_opposite (name_pair, &body) - name, opposite_name = name_pair.flatten.collect { |x| x.to_sym } - define_writer(name, &body) - define_opposite_writer(opposite_name => name) - end - - public :define_method - - def define_methods (*names, &body) - names.each { |name| define_method(name, &body) } - end - - def define_private_methods (*names, &body) - define_methods(*names, &body) - names.each { |name| private name } - end - - def define_protected_methods (*names, &body) - define_methods(*names, &body) - names.each { |name| protected name } - end - - def define_private_method (name, &body) - define_method(name, &body) - private name - end - - def define_protected_method (name, &body) - define_method(name, &body) - protected name - end -end - -class ImmutableAttributeError < StandardError - def initialize (attribute=nil, message=nil) - super message - @attribute = attribute - end - - define_accessors :attribute - - def to_s - if @attribute and @message - "cannot change the value of `#@attribute': #@message" - elsif @attribute - "cannot change the value of `#@attribute'" - elsif @message - "cannot change the value of attribute: #@message" - else - "cannot change the value of attribute" - end - end -end - -class Module - # Guard each of the specified attributes by replacing the writer - # method with a proxy that asks the supplied block before proceeding - # with the change. - # - # If it's okay to change the attribute, the block should return - # either nil or the symbol :mutable. If it isn't okay, the block - # should return a string saying why the attribute can't be changed. - # If you don't want to provide a reason, you can have the block - # return just the symbol :immutable. - def guard_writers(*names, &predicate) - for name in names.map { |x| x.to_sym } do - define_hard_alias("__unguarded_#{name.writer}" => name.writer) - define_method(name.writer) do |new_value| - case result = predicate.call - when :mutable, nil - __send__("__unguarded_#{name.writer}", new_value) - when :immutable - raise ImmutableAttributeError.new(name) - else - raise ImmutableAttributeError.new(name, result) - end - end - end - end - - def define_guarded_writers (*names, &block) - define_writers(*names) - guard_writers(*names, &block) - end - - define_soft_alias :guard_writer => :guard_writers - define_soft_alias :define_guarded_writer => :define_guarded_writers -end - -if __FILE__ == $0 - require "test/unit" - - class DefineAccessorsTest < Test::Unit::TestCase - def setup - @X = Class.new - @Y = Class.new @X - @x = @X.new - @y = @Y.new - end - - def test_define_hard_aliases - @X.define_method(:foo) { 123 } - @X.define_method(:baz) { 321 } - @X.define_hard_aliases :bar => :foo, :quux => :baz - assert_equal @x.foo, 123 - assert_equal @x.bar, 123 - assert_equal @y.foo, 123 - assert_equal @y.bar, 123 - assert_equal @x.baz, 321 - assert_equal @x.quux, 321 - assert_equal @y.baz, 321 - assert_equal @y.quux, 321 - @Y.define_method(:foo) { 456 } - assert_equal @y.foo, 456 - assert_equal @y.bar, 123 - @Y.define_method(:quux) { 654 } - assert_equal @y.baz, 321 - assert_equal @y.quux, 654 - end - - def test_define_soft_aliases - @X.define_method(:foo) { 123 } - @X.define_method(:baz) { 321 } - @X.define_soft_aliases :bar => :foo, :quux => :baz - assert_equal @x.foo, 123 - assert_equal @x.bar, 123 - assert_equal @y.foo, 123 - assert_equal @y.bar, 123 - assert_equal @x.baz, 321 - assert_equal @x.quux, 321 - assert_equal @y.baz, 321 - assert_equal @y.quux, 321 - @Y.define_method(:foo) { 456 } - assert_equal @y.foo, @y.bar, 456 - @Y.define_method(:quux) { 654 } - assert_equal @y.baz, 321 - assert_equal @y.quux, 654 - end - - def test_define_readers - @X.define_readers :foo, :bar - assert !@x.respond_to?(:foo=) - assert !@x.respond_to?(:bar=) - @x.instance_eval { @foo = 123 ; @bar = 456 } - assert_equal @x.foo, 123 - assert_equal @x.bar, 456 - @X.define_readers :baz?, :quux? - assert !@x.respond_to?(:baz=) - assert !@x.respond_to?(:quux=) - @x.instance_eval { @baz = false ; @quux = true } - assert !@x.baz? - assert @x.quux? - end - - def test_define_writers - assert !@X.writer_defined?(:foo) - assert !@X.writer_defined?(:bar) - @X.define_writers :foo, :bar - assert @X.writer_defined?(:foo) - assert @X.writer_defined?(:bar) - assert @X.writer_defined?(:foo=) - assert @X.writer_defined?(:bar=) - assert @X.writer_defined?(:foo?) - assert @X.writer_defined?(:bar?) - assert !@x.respond_to?(:foo) - assert !@x.respond_to?(:bar) - @x.foo = 123 - @x.bar = 456 - assert_equal @x.instance_eval { @foo }, 123 - assert_equal @x.instance_eval { @bar }, 456 - @X.define_writers :baz?, :quux? - assert !@x.respond_to?(:baz?) - assert !@x.respond_to?(:quux?) - @x.baz = true - @x.quux = false - assert_equal @x.instance_eval { @baz }, true - assert_equal @x.instance_eval { @quux }, false - end - - def test_define_accessors - @X.define_accessors :foo, :bar - @x.foo = 123 ; @x.bar = 456 - assert_equal @x.foo, 123 - assert_equal @x.bar, 456 - end - - def test_define_opposite_readers - @X.define_opposite_readers :foo? => :bar?, :baz? => :quux? - assert !@x.respond_to?(:foo=) - assert !@x.respond_to?(:bar=) - assert !@x.respond_to?(:baz=) - assert !@x.respond_to?(:quux=) - @x.instance_eval { @bar = true ; @quux = false } - assert !@x.foo? - assert @x.bar? - assert @x.baz? - assert !@x.quux? - end - - def test_define_opposite_writers - @X.define_opposite_writers :foo? => :bar?, :baz => :quux - end - end -end diff --git a/lib/puppet/event-loop/event-loop.rb b/lib/puppet/event-loop/event-loop.rb deleted file mode 100644 index 5d78844ef..000000000 --- a/lib/puppet/event-loop/event-loop.rb +++ /dev/null @@ -1,355 +0,0 @@ -## event-loop.rb --- high-level IO multiplexer -# Copyright (C) 2005 Daniel Brockman - -# This program is free software; you can redistribute it -# and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation; -# either version 2 of the License, or (at your option) any -# later version. - -# This file is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty -# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. - -# You should have received a copy of the GNU General Public -# License along with this program; if not, write to the Free -# Software Foundation, 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA. - -require "puppet/event-loop/better-definers" -require "puppet/event-loop/signal-system" - -require "fcntl" - -class EventLoop - include SignalEmitter - - IO_STATES = [:readable, :writable, :exceptional] - - class << self - def default ; @default ||= new end - def default= x ; @default = x end - - def current - Thread.current["event-loop::current"] || default end - def current= x - Thread.current["event-loop::current"] = x end - - def with_current (new) - if current == new - yield - else - begin - old = self.current - self.current = new - yield - ensure - self.current = old - end - end - end - - def method_missing (name, *args, &block) - if current.respond_to? name - current.__send__(name, *args, &block) - else - super - end - end - end - - define_signals :before_sleep, :after_sleep - - def initialize - @running = false - @awake = false - @wakeup_time = nil - @timers = [] - - @io_arrays = [[], [], []] - @ios = Hash.new do |h, k| raise ArgumentError, - "invalid IO event: #{k}", caller(2) end - IO_STATES.each_with_index { |x, i| @ios[x] = @io_arrays[i] } - - @notify_src, @notify_snk = IO.pipe - - @notify_src.will_block = false - @notify_snk.will_block = false - - # Each time a byte is sent through the notification pipe - # we need to read it, or IO.select will keep returning. - monitor_io(@notify_src, :readable) - @notify_src.extend(Watchable) - @notify_src.on_readable do - begin - @notify_src.sysread(256) - rescue Errno::EAGAIN - # The pipe wasn't readable after all. - end - end - end - - define_opposite_accessors \ - :stopped? => :running?, - :sleeping? => :awake? - - def run - if block_given? - thread = Thread.new { run } - yield ; quit ; thread.join - else - running! - iterate while running? - end - ensure - quit - end - - def iterate (user_timeout=nil) - t1, t2 = user_timeout, max_timeout - timeout = t1 && t2 ? [t1, t2].min : t1 || t2 - select(timeout).zip(IO_STATES) do |ios, state| - ios.each { |x| x.signal(state) } if ios - end - end - - private - - def select (timeout) - @wakeup_time = timeout ? Time.now + timeout : nil - # puts "waiting: #{timeout} seconds" - signal :before_sleep ; sleeping! - IO.select(*@io_arrays + [timeout]) || [] - ensure - awake! ; signal :after_sleep - @timers.each { |x| x.sound_alarm if x.ready? } - end - - public - - def quit ; stopped! ; wake_up ; self end - - def monitoring_io? (io, event) - @ios[event].include? io end - def monitoring_timer? (timer) - @timers.include? timer end - - def monitor_io (io, *events) - for event in events do - unless monitoring_io?(io, event) - @ios[event] << io ; wake_up - end - end - end - - def monitor_timer (timer) - unless monitoring_timer? timer - @timers << timer - end - end - - def check_timer (timer) - wake_up if timer.end_time < @wakeup_time - end - - def ignore_io (io, *events) - events = IO_STATES if events.empty? - for event in events do - wake_up if @ios[event].delete(io) - end - end - - def ignore_timer (timer) - # Don't need to wake up for this. - @timers.delete(timer) - end - - def max_timeout - return nil if @timers.empty? - [@timers.collect { |x| x.time_left }.min, 0].max - end - - def wake_up - @notify_snk.write('.') if sleeping? - end -end - -class Symbol - def io_state? - EventLoop::IO_STATES.include? self - end -end - -module EventLoop::Watchable - include SignalEmitter - - define_signals :readable, :writable, :exceptional - - def monitor_events (*events) - EventLoop.monitor_io(self, *events) end - def ignore_events (*events) - EventLoop.ignore_io(self, *events) end - - define_soft_aliases \ - :monitor_event => :monitor_events, - :ignore_event => :ignore_events - - def close ; super - ignore_events end - def close_read ; super - ignore_event :readable end - def close_write ; super - ignore_event :writable end - - module Automatic - include EventLoop::Watchable - - def add_signal_handler (name, &handler) super - monitor_event(name) if name.io_state? - end - - def remove_signal_handler (name, handler) super - if @signal_handlers[name].empty? - ignore_event(name) if name.io_state? - end - end - end -end - -class IO - def on_readable &block - extend EventLoop::Watchable::Automatic - on_readable(&block) - end - - def on_writable &block - extend EventLoop::Watchable::Automatic - on_writable(&block) - end - - def on_exceptional &block - extend EventLoop::Watchable::Automatic - on_exceptional(&block) - end - - def will_block? - require "fcntl" - fcntl(Fcntl::F_GETFL, 0) & Fcntl::O_NONBLOCK == 0 - end - - def will_block= (wants_blocking) - require "fcntl" - flags = fcntl(Fcntl::F_GETFL, 0) - if wants_blocking - flags &= ~Fcntl::O_NONBLOCK - else - flags |= Fcntl::O_NONBLOCK - end - fcntl(Fcntl::F_SETFL, flags) - end -end - -class EventLoop::Timer - include SignalEmitter - - DEFAULT_INTERVAL = 0.0 - DEFAULT_TOLERANCE = 0.001 - - def initialize (options={}, &handler) - @running = false - @start_time = nil - - if options.kind_of? Numeric - options = { :interval => options } - end - - if options[:interval] - @interval = options[:interval].to_f - else - @interval = DEFAULT_INTERVAL - end - - if options[:tolerance] - @tolerance = options[:tolerance].to_f - elsif DEFAULT_TOLERANCE < @interval - @tolerance = DEFAULT_TOLERANCE - else - @tolerance = 0.0 - end - - @event_loop = options[:event_loop] || EventLoop.current - - if block_given? - add_signal_handler(:alarm, &handler) - start unless options[:start?] == false - else - start if options[:start?] - end - end - - define_readers :interval, :tolerance - define_signal :alarm - - def stopped? ; @start_time == nil end - def running? ; @start_time != nil end - - def interval= (new_interval) - old_interval = @interval - @interval = new_interval - if new_interval < old_interval - @event_loop.check_timer(self) - end - end - - def end_time - @start_time + @interval end - def time_left - end_time - Time.now end - def ready? - time_left <= @tolerance end - - def restart - @start_time = Time.now - end - - def sound_alarm - signal :alarm - restart if running? - end - - def start - @start_time = Time.now - @event_loop.monitor_timer(self) - end - - def stop - @start_time = nil - @event_loop.ignore_timer(self) - end -end - -if __FILE__ == $0 - require "test/unit" - - class TimerTest < Test::Unit::TestCase - def setup - @timer = EventLoop::Timer.new(:interval => 0.001) - end - - def test_timer - @timer.on_alarm do - puts "[#{@timer.time_left} seconds left after alarm]" - EventLoop.quit - end - 8.times do - t0 = Time.now - @timer.start ; EventLoop.run - t1 = Time.now - assert(t1 - t0 > @timer.interval - @timer.tolerance) - end - end - end -end - -## event-loop.rb ends here. diff --git a/lib/puppet/event-loop/signal-system.rb b/lib/puppet/event-loop/signal-system.rb deleted file mode 100644 index f7fe9b52c..000000000 --- a/lib/puppet/event-loop/signal-system.rb +++ /dev/null @@ -1,220 +0,0 @@ -## signal-system.rb --- simple intra-process signal system -# Copyright (C) 2005 Daniel Brockman - -# This program is free software; you can redistribute it -# and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation; -# either version 2 of the License, or (at your option) any -# later version. - -# This file is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty -# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. - -# You should have received a copy of the GNU General Public -# License along with this program; if not, write to the Free -# Software Foundation, 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA. - -require "puppet/event-loop/better-definers" - -module SignalEmitterModule - def self.extended (object) - if object.kind_of? Module and not object < SignalEmitter - if object.respond_to? :fcall - # This is the way to call private methods - # in Ruby 1.9 as of November 16. - object.fcall :include, SignalEmitter - else - object.__send__ :include, SignalEmitter - end - end - end - - def define_signal (name, slot=:before, &body) - # Can't use `define_method' and take a block pre-1.9. - class_eval %{ def on_#{name} &block - add_signal_handler(:#{name}, &block) end } - define_signal_handler(name, :before, &lambda {|*a|}) - define_signal_handler(name, :after, &lambda {|*a|}) - define_signal_handler(name, slot, &body) if block_given? - end - - def define_signals (*names, &body) - names.each { |x| define_signal(x, &body) } - end - - def define_signal_handler (name, slot=:before, &body) - case slot - when :before - define_protected_method "handle_#{name}", &body - when :after - define_protected_method "after_handle_#{name}", &body - else - raise ArgumentError, "invalid slot `#{slot.inspect}'; " + - "should be `:before' or `:after'", caller(1) - end - end -end - -# This is an old name for the same thing. -SignalEmitterClass = SignalEmitterModule - -module SignalEmitter - def self.included (includer) - if not includer.kind_of? SignalEmitterClass - includer.extend SignalEmitterClass - end - end - - def __maybe_initialize_signal_emitter - @signal_handlers ||= Hash.new { |h, k| h[k] = Array.new } - @allow_dynamic_signals ||= false - end - - define_accessors :allow_dynamic_signals? - - def add_signal_handler (name, &handler) - __maybe_initialize_signal_emitter - @signal_handlers[name] << handler - return handler - end - - define_soft_aliases [:on, :on_signal] => :add_signal_handler - - def remove_signal_handler (name, handler) - __maybe_initialize_signal_emitter - @signal_handlers[name].delete(handler) - end - - def __signal__ (name, *args, &block) - __maybe_initialize_signal_emitter - respond_to? "on_#{name}" or allow_dynamic_signals? or - fail "undefined signal `#{name}' for #{self}:#{self.class}" - __send__("handle_#{name}", *args, &block) if - respond_to? "handle_#{name}" - @signal_handlers[name].each { |x| x.call(*args, &block) } - __send__("after_handle_#{name}", *args, &block) if - respond_to? "after_handle_#{name}" - end - - define_soft_alias :signal => :__signal__ -end - -# This module is indended to be a convenience mixin to be used by -# classes whose objects need to observe foreign signals. That is, -# if you want to observe some signals coming from an object, *you* -# should mix in this module. -# -# You cannot use this module at two different places of the same -# inheritance chain to observe signals coming from the same object. -# -# XXX: This has not seen much use, and I'd like to provide a -# better solution for the problem in the future. -module SignalObserver - def __maybe_initialize_signal_observer - @observed_signals ||= Hash.new do |signals, object| - signals[object] = Hash.new do |handlers, name| - handlers[name] = Array.new - end - end - end - - def observe_signal (subject, name, &handler) - __maybe_initialize_signal_observer - @observed_signals[subject][name] << handler - subject.add_signal_handler(name, &handler) - end - - def map_signals (source, pairs={}) - pairs.each do |src_name, dst_name| - observe_signal(source, src_name) do |*args| - __signal__(dst_name, *args) - end - end - end - - def absorb_signals (subject, *names) - names.each do |name| - observe_signal(subject, name) do |*args| - __signal__(name, *args) - end - end - end - - define_soft_aliases \ - :map_signal => :map_signals, - :absorb_signal => :absorb_signals - - def ignore_signal (subject, name) - __maybe_initialize_signal_observer - __ignore_signal_1(subject, name) - @observed_signals.delete(subject) if - @observed_signals[subject].empty? - end - - def ignore_signals (subject, *names) - __maybe_initialize_signal_observer - names = @observed_signals[subject] if names.empty? - names.each { |x| __ignore_signal_1(subject, x) } - end - - private - - def __ignore_signal_1(subject, name) - @observed_signals[subject][name].each do |handler| - subject.remove_signal_handler(name, handler) end - @observed_signals[subject].delete(name) - end -end - -if __FILE__ == $0 - require "test/unit" - class SignalEmitterTest < Test::Unit::TestCase - class X - include SignalEmitter - define_signal :foo - end - - def setup - @x = X.new - end - - def test_on_signal - moomin = 0 - @x.on_signal(:foo) { moomin = 1 } - @x.signal :foo - assert moomin == 1 - end - - def test_on_foo - moomin = 0 - @x.on_foo { moomin = 1 } - @x.signal :foo - assert moomin == 1 - end - - def test_multiple_on_signal - moomin = 0 - @x.on_signal(:foo) { moomin += 1 } - @x.on_signal(:foo) { moomin += 2 } - @x.on_signal(:foo) { moomin += 4 } - @x.on_signal(:foo) { moomin += 8 } - @x.signal :foo - assert moomin == 15 - end - - def test_multiple_on_foo - moomin = 0 - @x.on_foo { moomin += 1 } - @x.on_foo { moomin += 2 } - @x.on_foo { moomin += 4 } - @x.on_foo { moomin += 8 } - @x.signal :foo - assert moomin == 15 - end - end -end - -## application-signals.rb ends here. |