summaryrefslogtreecommitdiff
path: root/lib/singleton.rb
diff options
context:
space:
mode:
authormatz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2001-11-27 10:00:35 +0000
committermatz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2001-11-27 10:00:35 +0000
commit186c8b592a8b5badfd42ff7295c5edb44ef1abdd (patch)
tree922c6ba9baaf7b140efadfb387c0a0780e544eb6 /lib/singleton.rb
parent09a4937b2fcf883fa57af1dfa5d12f605f812970 (diff)
* marshal.c (w_float): must distinguish -0.0 from 0.0.
* gc.c (gc_mark_all): tweak mark order for little bit better scan. * gc.c (rb_gc_mark): ditto. * gc.c (rb_gc): ditto. * enum.c (sort_by_i): slight performance boost. * gc.c (gc_mark_rest): should call gc_mark_children(), not gc_mark(). * gc.c (rb_gc_mark): may cause infinite looop. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1861 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/singleton.rb')
-rw-r--r--lib/singleton.rb230
1 files changed, 188 insertions, 42 deletions
diff --git a/lib/singleton.rb b/lib/singleton.rb
index 1945c4446b..696d37c0da 100644
--- a/lib/singleton.rb
+++ b/lib/singleton.rb
@@ -1,49 +1,195 @@
-# Singleton module that ensures only one object to be allocated.
-#
-# Usage:
-# class SomeSingletonClass
-# include Singleton
-# #....
-# end
-# a = SomeSingletonClass.instance
-# b = SomeSingletonClass.instance # a and b are same object
-# p [a,b]
-# a = SomeSingletonClass.new # error (`new' is private)
-
+# The Singleton module implements the Singleton pattern - i.e.
+#
+# class Klass
+# include Singleton
+# # ...
+# end
+#
+# * ensures that only one instance of Klass called ``the instance''
+# can be created.
+#
+# a,b = Klass.instance, Klass.instance
+# a == b # => true
+# a.new # NoMethodError - new is private ...
+#
+# * ``The instance'' is created at instanciation time - i.e. the first call
+# of Klass.instance().
+#
+# class OtherKlass
+# include Singleton
+# # ...
+# end
+# p "#{ObjectSpace.each_object(OtherKlass) {}}" # => 0
+#
+# * This behavior is preserved under inheritance.
+#
+#
+# This achieved by marking
+# * Klass.new and Klass.allocate - as private and
+# * Klass.inherited(sub_klass) - modifying to ensure
+# that the Singleton pattern is properly inherited.
+#
+# In addition Klass is provided with the class methods
+# * Klass.instance() - returning ``the instance''
+# * Klass._load(str) - returning ``the instance''
+# * Klass._wait() - a hook method putting a second (or n-th)
+# thread calling Klass.instance on a waiting loop if the first call
+# to Klass.instance is still in progress.
+#
+# The sole instance method of Singleton is
+# * _dump(depth) - returning the empty string
+# The default Marshalling strategy is to strip all state information - i.e.
+# instance variables from ``the instance''. Providing costume
+# _dump(depth) and _load(str) method allows the (partial) resurrection
+# of a previous state of ``the instance'' - see third example.
+#
module Singleton
- def Singleton.included(klass)
- klass.private_class_method(:new)
- klass.instance_eval %{
- @__instance__ = nil
- def instance
- if defined? @__allocating__
- until @__instance__
- sleep 0.5
- end
- elsif ! @__instance__
- Thread.critical = true
- @__allocating__ = true
- Thread.critical = false
- begin
- @__instance__ = new
- ensure
- remove_instance_variable(:@__allocating__)
- end
- end
- return @__instance__
- end
- }
- end
+ def Singleton.included (klass)
+ # should this be checked?
+ # raise TypeError.new "..." if klass.type == Module
+ class << klass
+ def inherited(sub_klass)
+ # @__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
+ @__instance__ = nil
+ def sub_klass.instance
+ unless @__instance__.nil?
+ # is the extra flexiblity having the hook method
+ # _wait() around ever useful?
+ _wait() while false.equal?(@__instance__)
+ # check for instance creation
+ return @__instance__ if @__instance__
+ end
+ Thread.critical = true
+ unless @__instance__
+ @__instance__ = false
+ Thread.critical = false
+ begin
+ @__instance__ = new
+ ensure
+ if @__instance__
+ define_method(:instance) {@__instance__ }
+ else
+ # failed instance creation
+ @__instance__ = nil
+ end
+ end
+ else
+ Thread.critical = false
+ end
+ return @__instance__
+ end
+ end
+ def _load(str)
+ instance
+ end
+ def _wait
+ sleep(0.05)
+ end
+ private :new, :allocate
+ # hook methods are also marked private
+ private :_load,:_wait
+ end
+ klass.inherited klass
+ end
+ private
+ def _dump(depth)
+ return ""
+ end
end
if __FILE__ == $0
- class SomeSingletonClass
+
+#basic example
+class SomeSingletonClass
include Singleton
- #....
- end
+end
+a = SomeSingletonClass.instance
+b = SomeSingletonClass.instance # a and b are same object
+p a == b # => true
+begin
+ SomeSingletonClass.new
+rescue NoMethodError => mes
+ puts mes
+end
+
+# threaded example with exception
+Thread.abort_on_exception = true
+class Ups < SomeSingletonClass
+ @__threads__= []
+ @__flip__ = nil
+ @@__index__ = nil
+
+ def initialize
+ sleep(rand(0.1)/10.0)
+ Thread.current[:index] = @@__index__
+ end
+ class << self
+ def allocate
+ unless @__flip__
+ @__flip__ = true
+ raise "boom - allocation in thread ##{@@__index__} aborted"
+ end
+ super()
+ end
+ def instanciate_all
+ 1.upto(5) do |@@__index__|
+ sleep(rand(0.1)/10.0)
+ @__threads__.push Thread.new {
+ begin
+ instance
+ rescue RuntimeError => mes
+ puts mes
+ end
+ }
+ end
+ end
+ def join
+ @__threads__.each do |t|
+ t.join
+ puts "initialize called by thread ##{t[:index]}" if
+t[:index]
+ end
+ end
+ end
+end
+
+
+puts "There is(are) #{ObjectSpace.each_object(Ups) {}} Ups instance(s)"
+ # => The is(are) 0 Ups instance(s)
+Ups.instanciate_all
+Ups.join # => initialize called by thread # i - where i = 2 ... 5
+p Marshal.load(Marshal.dump(Ups.instance)) == Ups.instance # => true
+puts "There is(are) #{ObjectSpace.each_object(Ups) {}} Ups instance(s)"
+ # => The is(are) 1 Ups instance(s)
+
+# 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
+end
+def A._load(str)
+ instance.persist = Marshal.load(str)
+ instance
+end
+
+a = A.instance
+a.persist = ["persist"]
+a.die = "die"
+
+stored_state = Marshal.dump(a)
+# change state
+a.persist = nil
+a.die = nil
+b = Marshal.load(stored_state)
+p a == b # => true
+p a.persist # => ["persist"]
+p a.die # => nil
- a = SomeSingletonClass.instance
- b = SomeSingletonClass.instance # a and b are same object
- p [a,b]
- a = SomeSingletonClass.new # error (`new' is private)
end