From 2c225e77e0521f91477b0f470df0a788a199d3c1 Mon Sep 17 00:00:00 2001 From: matz Date: Sat, 2 Aug 2003 18:05:02 +0000 Subject: * numeric.c (flo_to_s): get rid of buffer overflow. * io.c (appendline): clearerr(3) before raising exception, since exception may be captured by rescue. [ruby-talk:77794] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4290 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/singleton.rb | 456 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 237 insertions(+), 219 deletions(-) (limited to 'lib/singleton.rb') diff --git a/lib/singleton.rb b/lib/singleton.rb index 939159496b..30990b79bd 100644 --- a/lib/singleton.rb +++ b/lib/singleton.rb @@ -11,10 +11,10 @@ # # a,b = Klass.instance, Klass.instance # a == b # => true -# a.new # NoMethodError - new is private ... +# a.new # NoMethodError - new is private ... # -# * ``The instance'' is created at instanciation time, in other words -# the first call of Klass.instance(), thus +# * ``The instance'' is created at instanciation time, in other +# words the first call of Klass.instance(), thus # # class OtherKlass # include Singleton @@ -25,148 +25,157 @@ # * This behavior is preserved under inheritance and cloning. # # +# # This is achieved by marking # * Klass.new and Klass.allocate - as private -# * removing #clone and #dup and modifying +# +# Providing (or modifying) the class methods # * Klass.inherited(sub_klass) and Klass.clone() - # to ensure that the Singleton pattern is properly # inherited and cloned. # -# In addition Klass is providing the additional class methods -# * Klass.instance() - returning ``the instance''. After a successful -# self modifying instanciating first call the method body is a simple -# def Klass.instance() -# return @__instance__ -# end -# * Klass._load(str) - calls instance() -# * Klass._instanciate?() - returning ``the instance'' or nil -# This hook method puts a second (or nth) thread calling -# Klass.instance() on a waiting loop. The return value signifies -# the successful completion or premature termination of the -# first, or more generally, current instanciating thread. +# * Klass.instance() - returning ``the instance''. After a +# successful self modifying (normally the first) call the +# method body is a simple: +# +# def Klass.instance() +# return @__instance__ +# end +# +# * Klass._load(str) - calling Klass.instance() +# +# * Klass._instanciate?() - returning ``the instance'' or +# nil. This hook method puts a second (or nth) thread calling +# Klass.instance() on a waiting loop. The return value +# signifies the successful completion or premature termination +# of the first, or more generally, current "instanciation thread". +# +# +# The instance method of Singleton are +# * clone and dup - raising TypeErrors to prevent cloning or duping # -# The sole instance method of Singleton is # * _dump(depth) - returning the empty string. Marshalling strips -# by default all state information, e.g. instance variables and taint -# state, from ``the instance''. Providing custom _load(str) and -# _dump(depth) hooks allows the (partially) resurrections of a -# previous state of ``the instance''. +# by default all state information, e.g. instance variables and +# taint state, from ``the instance''. Providing custom _load(str) +# and _dump(depth) hooks allows the (partially) resurrections of +# a previous state of ``the instance''. + + module Singleton - private - # default marshalling strategy - def _dump(depth=-1) '' end - - class << self - # extending an object with Singleton is a bad idea - undef_method :extend_object - private - def append_features(mod) - # This catches ill advisted inclusions of Singleton in - # singletons types (sounds like an oxymoron) and - # helps out people counting on transitive mixins - unless mod.instance_of?(Class) - raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}" - end - unless (class << mod; self end) <= (class << Object; self end) - raise TypeError, "Inclusion of the OO-Singleton module in singleton type" - end - super + # disable build-in copying methods + def clone + raise TypeError, "can't clone instance of singleton #{self.class}" + end + def dup + raise TypeError, "can't dup instance of singleton #{self.class}" + end + + private + # default marshalling strategy + def _dump(depth=-1) + '' + end +end + + +class << Singleton + # Method body of first instance call. + FirstInstanceCall = proc do + # @__instance__ takes on one of the following values + # * nil - before and after a failed creation + # * false - during creation + # * sub_class instance - after a successful creation + # the form makes up for the lack of returns in progs + Thread.critical = true + if @__instance__.nil? + @__instance__ = false + Thread.critical = false + begin + @__instance__ = new + ensure + if @__instance__ + def self.instance; @__instance__ end + else + @__instance__ = nil # failed instance creation end - def included(klass) - # remove build in copying methods - klass.class_eval do - define_method(:clone) {raise TypeError, "can't clone singleton #{self.class}"} - end - - # initialize the ``klass instance variable'' @__instance__ to nil - klass.instance_eval do @__instance__ = nil end - class << klass - # a main point of the whole exercise - make - # new and allocate private - private :new, :allocate - - # declare the self modifying klass#instance method - define_method(:instance, Singleton::FirstInstanceCall) - - # simple waiting loop hook - should do in most cases - # note the pre/post-conditions of a thread-critical state - private - def _instanciate?() - while false.equal?(@__instance__) - Thread.critical = false - sleep(0.08) - Thread.critical = true - end - @__instance__ - end - - # default Marshalling strategy - def _load(str) instance end - - # ensure that the Singleton pattern is properly inherited - def inherited(sub_klass) - super - sub_klass.instance_eval do @__instance__ = nil end - class << sub_klass - define_method(:instance, Singleton::FirstInstanceCall) - end - end - - public - # properly clone the Singleton pattern. Question - Did - # you know that duping doesn't copy class methods? - def clone - res = super - res.instance_eval do @__instance__ = nil end - class << res - define_method(:instance, Singleton::FirstInstanceCall) - end - res - end - end # of << klass - end # of included - end # of << Singleton - - FirstInstanceCall = proc do - # @__instance__ takes on one of the following values - # * nil - before and after a failed creation - # * false - during creation - # * sub_class instance - after a successful creation - # the form makes up for the lack of returns in progs - Thread.critical = true - if @__instance__.nil? - @__instance__ = false - Thread.critical = false - begin - @__instance__ = new - ensure - if @__instance__ - define_method(:instance) {@__instance__} - else - @__instance__ = nil # failed instance creation - end - end - elsif _instanciate?() - Thread.critical = false + end + elsif _instanciate?() + Thread.critical = false + else + @__instance__ = false + Thread.critical = false + begin + @__instance__ = new + ensure + if @__instance__ + def self.instance; @__instance__ end else - @__instance__ = false - Thread.critical = false - begin - @__instance__ = new - ensure - if @__instance__ - define_method(:instance) {@__instance__} - else - @__instance__ = nil - end - end + @__instance__ = nil end - @__instance__ + end + end + @__instance__ + end + + module SingletonClassMethods + # properly clone the Singleton pattern - did you know + # that duping doesn't copy class methods? + def clone + Singleton.__init__(super) + end + + private + + # ensure that the Singleton pattern is properly inherited + def inherited(sub_klass) + super + Singleton.__init__(sub_klass) + end + + def _load(str) + instance + end + + # waiting-loop hook + def _instanciate?() + while false.equal?(@__instance__) + Thread.critical = false + sleep(0.08) # timeout + Thread.critical = true + end + @__instance__ end + end + + def __init__(klass) + klass.instance_eval { @__instance__ = nil } + class << klass + define_method(:instance,FirstInstanceCall) + end + klass + end + + private + # extending an object with Singleton is a bad idea + undef_method :extend_object + + def append_features(mod) + # help out people counting on transitive mixins + unless mod.instance_of?(Class) + raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}" + end + super + end + + def included(klass) + super + klass.private_class_method :new, :allocate + klass.extend SingletonClassMethods + Singleton.__init__(klass) + end end - - + if __FILE__ == $0 @@ -175,11 +184,10 @@ def num_of_instances(klass) "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)" end -# The basic and most important example. The latter examples demonstrate -# advanced features that have no relevance for the general usage +# The basic and most important example. class SomeSingletonClass - include Singleton + include Singleton end puts "There are #{num_of_instances(SomeSingletonClass)}" @@ -188,9 +196,9 @@ b = SomeSingletonClass.instance # a and b are same object puts "basic test is #{a == b}" begin - SomeSingletonClass.new + SomeSingletonClass.new rescue NoMethodError => mes - puts mes + puts mes end @@ -199,83 +207,102 @@ puts "\nThreaded example with exception and customized #_instanciate?() hook"; p Thread.abort_on_exception = false class Ups < SomeSingletonClass - def initialize - self.class.__sleep - puts "initialize called by thread ##{Thread.current[:i]}" + def initialize + self.class.__sleep + puts "initialize called by thread ##{Thread.current[:i]}" + end +end + +class << Ups + def _instanciate? + @enter.push Thread.current[:i] + while false.equal?(@__instance__) + Thread.critical = false + sleep 0.08 + Thread.critical = true end - class << self - def _instanciate? - @enter.push Thread.current[:i] - while false.equal?(@__instance__) - Thread.critical = false - sleep 0.04 - Thread.critical = true - end - @leave.push Thread.current[:i] - @__instance__ - end - def __sleep - sleep(rand(0.08)) - end - def allocate - __sleep - def self.allocate; __sleep; super() end - raise "boom - allocation in thread ##{Thread.current[:i]} aborted" - end - def instanciate_all - @enter = [] - @leave = [] - 1.upto(9) do |i| - Thread.new do - begin - Thread.current[:i] = i - __sleep - instance - rescue RuntimeError => mes - puts mes - end - end - end - puts "Before there were #{num_of_instances(self)}" - sleep 5 - puts "Now there is #{num_of_instances(self)}" - puts "#{@enter.join '; '} was the order of threads entering the waiting loop" - puts "#{@leave.join '; '} was the order of threads leaving the waiting loop" - end + @leave.push Thread.current[:i] + @__instance__ + end + + def __sleep + sleep(rand(0.08)) + end + + def new + begin + __sleep + raise "boom - thread ##{Thread.current[:i]} failed to create instance" + ensure + # simple flip-flop + class << self + remove_method :new + end end + end + + def instanciate_all + @enter = [] + @leave = [] + 1.upto(9) {|i| + Thread.new { + begin + Thread.current[:i] = i + __sleep + instance + rescue RuntimeError => mes + puts mes + end + } + } + puts "Before there were #{num_of_instances(self)}" + sleep 3 + puts "Now there is #{num_of_instances(self)}" + puts "#{@enter.join '; '} was the order of threads entering the waiting loop" + puts "#{@leave.join '; '} was the order of threads leaving the waiting loop" + end end + Ups.instanciate_all -# results in message like -# Before there were 0 Ups instances -# boom - allocation in thread #8 aborted -# initialize called by thread #3 -# Now there is 1 Ups instance -# 2; 3; 6; 1; 7; 5; 9; 4 was the order of threads entering the waiting loop -# 3; 2; 1; 7; 6; 5; 4; 9 was the order of threads leaving the waiting loop +# results in message like +# Before there were 0 Ups instance(s) +# boom - thread #6 failed to create instance +# initialize called by thread #3 +# Now there is 1 Ups instance(s) +# 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop +# 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop + puts "\nLets see if class level cloning really works" Yup = Ups.clone -def Yup.allocate +def Yup.new + begin __sleep - def self.allocate; __sleep; super() end - raise "boom - allocation in thread ##{Thread.current[:i]} aborted" + raise "boom - thread ##{Thread.current[:i]} failed to create instance" + ensure + # simple flip-flop + class << self + remove_method :new + end + end end Yup.instanciate_all -puts "\n","Customized marshalling" +puts "\n\n","Customized marshalling" class A - include Singleton - attr_accessor :persist, :die - def _dump(depth) - # this strips the @die information from the instance - Marshal.dump(@persist,depth) - end + include Singleton + attr_accessor :persist, :die + def _dump(depth) + # this strips the @die information from the instance + Marshal.dump(@persist,depth) + end end + def A._load(str) - instance.persist = Marshal.load(str) - instance + instance.persist = Marshal.load(str) + instance end a = A.instance @@ -292,45 +319,36 @@ p a == b # => true p a.persist # => ["persist"] p a.die # => nil + puts "\n\nSingleton with overridden default #inherited() hook" class Up - def Up.inherited(sub_klass) - puts "#{sub_klass} subclasses #{self}" - end +end +def Up.inherited(sub_klass) + puts "#{sub_klass} subclasses #{self}" end class Middle < Up - undef_method :dup - include Singleton + include Singleton end -class Down < Middle; end - -puts "basic test is #{Down.instance == Down.instance}" - -puts "\n","Various exceptions" +class Down < Middle; end -begin - module AModule - include Singleton - end -rescue TypeError => mes - puts mes #=> Inclusion of the OO-Singleton module in module AModule -end +puts "and basic \"Down test\" is #{Down.instance == Down.instance}\n +Various exceptions" begin - class << 'aString' - include Singleton - end + module AModule + include Singleton + end rescue TypeError => mes - puts mes # => Inclusion of the OO-Singleton module in singleton type + puts mes #=> Inclusion of the OO-Singleton module in module AModule end begin - 'aString'.extend Singleton + 'aString'.extend Singleton rescue NoMethodError => mes - puts mes #=> undefined method `extend_object' for Singleton:Module + puts mes #=> undefined method `extend_object' for Singleton:Module end end -- cgit v1.2.3