30
30
# * Klass.new and Klass.allocate - as private
32
32
# Providing (or modifying) the class methods
33
# * Klass.inherited(sub_klass) and Klass.clone() -
33
# * Klass.inherited(sub_klass) and Klass.clone() -
34
34
# to ensure that the Singleton pattern is properly
35
35
# inherited and cloned.
45
45
# * Klass._load(str) - calling Klass.instance()
47
# * Klass._instantiate?() - returning ``the instance'' or
48
# nil. This hook method puts a second (or nth) thread calling
49
# Klass.instance() on a waiting loop. The return value
50
# signifies the successful completion or premature termination
51
# of the first, or more generally, current "instantiation thread".
48
54
# The instance method of Singleton are
49
55
# * clone and dup - raising TypeErrors to prevent cloning or duping
65
71
raise TypeError, "can't dup instance of singleton #{self.class}"
69
74
# default marshalling strategy
77
module SingletonClassMethods
82
# Method body of first instance call.
83
FirstInstanceCall = proc do
84
# @__instance__ takes on one of the following values
85
# * nil - before and after a failed creation
86
# * false - during creation
87
# * sub_class instance - after a successful creation
88
# the form makes up for the lack of returns in progs
89
Thread.critical = true
92
Thread.critical = false
98
remove_method :instance
99
def instance; @__instance__ end
102
@__instance__ = nil # failed instance creation
105
elsif _instantiate?()
106
Thread.critical = false
108
@__instance__ = false
109
Thread.critical = false
115
remove_method :instance
116
def instance; @__instance__ end
126
module SingletonClassMethods
78
127
# properly clone the Singleton pattern - did you know
79
# that duping doesn't copy class methods?
128
# that duping doesn't copy class methods?
81
130
Singleton.__init__(super)
86
# ensure that the Singleton pattern is properly inherited
139
# ensure that the Singleton pattern is properly inherited
87
140
def inherited(sub_klass)
89
142
Singleton.__init__(sub_klass)
147
while false.equal?(@__instance__)
148
Thread.critical = false
149
sleep(0.08) # timeout
150
Thread.critical = true
97
156
def __init__(klass)
98
157
klass.instance_eval { @__instance__ = nil }
100
# the mutex can get GCed once "instance" is redefined
103
(class << klass ; self ; end).instance_eval do
104
define_method(:instance) do ||
106
# note that there is no good way to support the _instantiate? hook
107
# in a backwards-compatible way without forcing the use of
108
# Thread.critical, which is the very thing we are trying to avoid
115
# redefining the method establishes a happens-before edge to
116
# callers of the redefined method, so that they will see a
117
# properly initialized @__instance__ without needing to
118
# synchronize on the mutex
120
def instance ; @__instance__ ; end
159
define_method(:instance,FirstInstanceCall)
131
165
# extending an object with Singleton is a bad idea
132
166
undef_method :extend_object
134
168
def append_features(mod)
135
169
# help out people counting on transitive mixins
136
170
unless mod.instance_of?(Class)
146
180
Singleton.__init__(klass)
152
186
if __FILE__ == $0
154
188
def num_of_instances(klass)
155
189
"#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
158
192
# The basic and most important example.
160
194
class SomeSingletonClass
161
195
include Singleton
163
puts "There are #{num_of_instances(SomeSingletonClass)}"
197
puts "There are #{num_of_instances(SomeSingletonClass)}"
165
199
a = SomeSingletonClass.instance
166
200
b = SomeSingletonClass.instance # a and b are same object