Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 314 lines (267 sloc) 7.271 kb
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
1 # The Singleton module implements the Singleton pattern.
2 #
3 # Usage:
4 # class Klass
5 # include Singleton
6 # # ...
7 # end
8 #
9 # * this ensures that only one instance of Klass lets call it
10 # ``the instance'' can be created.
11 #
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
12 # a,b = Klass.instance, Klass.instance
13 # a == b # => true
14 # Klass.new # NoMethodError - new is private ...
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
15 #
16 # * ``The instance'' is created at instantiation time, in other
17 # words the first call of Klass.instance(), thus
18 #
19 # class OtherKlass
20 # include Singleton
21 # # ...
22 # end
23 # ObjectSpace.each_object(OtherKlass){} # => 0.
24 #
25 # * This behavior is preserved under inheritance and cloning.
26 #
27 #
28 #
29 # This is achieved by marking
30 # * Klass.new and Klass.allocate - as private
31 #
32 # Providing (or modifying) the class methods
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
33 # * Klass.inherited(sub_klass) and Klass.clone() -
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
34 # to ensure that the Singleton pattern is properly
35 # inherited and cloned.
36 #
37 # * Klass.instance() - returning ``the instance''. After a
38 # successful self modifying (normally the first) call the
39 # method body is a simple:
40 #
41 # def Klass.instance()
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
42 # return @singleton__instance__
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
43 # end
44 #
45 # * Klass._load(str) - calling Klass.instance()
46 #
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".
52 #
53 #
54 # The instance method of Singleton are
55 # * clone and dup - raising TypeErrors to prevent cloning or duping
56 #
57 # * _dump(depth) - returning the empty string. Marshalling strips
58 # by default all state information, e.g. instance variables and
59 # taint state, from ``the instance''. Providing custom _load(str)
60 # and _dump(depth) hooks allows the (partially) resurrections of
61 # a previous state of ``the instance''.
62
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
63 require 'thread'
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
64
65 module Singleton
66 # disable build-in copying methods
67 def clone
68 raise TypeError, "can't clone instance of singleton #{self.class}"
69 end
70 def dup
71 raise TypeError, "can't dup instance of singleton #{self.class}"
72 end
055d754 Evan Phoenix A boatload of fixes done while getting flexmock and rake running
evanphx authored
73
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
74 # default marshalling strategy
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
75 def _dump(depth = -1)
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
76 ''
77 end
78
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
79 module SingletonClassMethods
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
80 # properly clone the Singleton pattern - did you know
81 # that duping doesn't copy class methods?
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
82 def clone
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
83 Singleton.__init__(super)
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
84 end
055d754 Evan Phoenix A boatload of fixes done while getting flexmock and rake running
evanphx authored
85
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
86 def _load(str)
87 instance
88 end
89
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
90 private
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
91
92 # ensure that the Singleton pattern is properly inherited
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
93 def inherited(sub_klass)
94 super
95 Singleton.__init__(sub_klass)
96 end
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
97 end
055d754 Evan Phoenix A boatload of fixes done while getting flexmock and rake running
evanphx authored
98
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
99 class << Singleton
100 def __init__(klass)
101 klass.instance_eval {
102 @singleton__instance__ = nil
103 @singleton__mutex__ = Mutex.new
104 }
105 def klass.instance
106 return @singleton__instance__ if @singleton__instance__
107 @singleton__mutex__.synchronize {
108 return @singleton__instance__ if @singleton__instance__
109 @singleton__instance__ = new()
110 }
111 @singleton__instance__
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
112 end
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
113 klass
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
114 end
055d754 Evan Phoenix A boatload of fixes done while getting flexmock and rake running
evanphx authored
115
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
116 private
117
118 # extending an object with Singleton is a bad idea
119 undef_method :extend_object
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
120
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
121 def append_features(mod)
122 # help out people counting on transitive mixins
123 unless mod.instance_of?(Class)
124 raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}"
125 end
126 super
127 end
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
128
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
129 def included(klass)
130 super
131 klass.private_class_method :new, :allocate
132 klass.extend SingletonClassMethods
133 Singleton.__init__(klass)
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
134 end
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
135 end
136
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
137 end
138
139
140 if __FILE__ == $0
141
142 def num_of_instances(klass)
143 "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
144 end
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
145
146 # The basic and most important example.
147
148 class SomeSingletonClass
149 include Singleton
150 end
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
151 puts "There are #{num_of_instances(SomeSingletonClass)}"
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
152
153 a = SomeSingletonClass.instance
154 b = SomeSingletonClass.instance # a and b are same object
155 puts "basic test is #{a == b}"
156
157 begin
158 SomeSingletonClass.new
159 rescue NoMethodError => mes
160 puts mes
161 end
162
163
164
165 puts "\nThreaded example with exception and customized #_instantiate?() hook"; p
166 Thread.abort_on_exception = false
167
168 class Ups < SomeSingletonClass
169 def initialize
170 self.class.__sleep
171 puts "initialize called by thread ##{Thread.current[:i]}"
172 end
173 end
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
174
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
175 class << Ups
176 def _instantiate?
177 @enter.push Thread.current[:i]
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
178 while false.equal?(@singleton__instance__)
179 @singleton__mutex__.unlock
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
180 sleep 0.08
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
181 @singleton__mutex__.lock
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
182 end
183 @leave.push Thread.current[:i]
7afc721 replace singleton.rb with 1.9 implementation to fix race conditions
Chuck Remes authored
184 @singleton__instance__
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
185 end
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
186
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
187 def __sleep
188 sleep(rand(0.08))
189 end
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
190
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
191 def new
192 begin
193 __sleep
194 raise "boom - thread ##{Thread.current[:i]} failed to create instance"
195 ensure
196 # simple flip-flop
197 class << self
198 remove_method :new
199 end
200 end
201 end
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
202
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
203 def instantiate_all
204 @enter = []
205 @leave = []
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
206 1.upto(9) {|i|
207 Thread.new {
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
208 begin
209 Thread.current[:i] = i
210 __sleep
211 instance
212 rescue RuntimeError => mes
213 puts mes
214 end
215 }
216 }
217 puts "Before there were #{num_of_instances(self)}"
218 sleep 3
219 puts "Now there is #{num_of_instances(self)}"
220 puts "#{@enter.join '; '} was the order of threads entering the waiting loop"
221 puts "#{@leave.join '; '} was the order of threads leaving the waiting loop"
222 end
223 end
224
225
226 Ups.instantiate_all
227 # results in message like
228 # Before there were 0 Ups instance(s)
229 # boom - thread #6 failed to create instance
230 # initialize called by thread #3
231 # Now there is 1 Ups instance(s)
232 # 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop
233 # 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop
234
235
236 puts "\nLets see if class level cloning really works"
237 Yup = Ups.clone
238 def Yup.new
239 begin
240 __sleep
241 raise "boom - thread ##{Thread.current[:i]} failed to create instance"
242 ensure
243 # simple flip-flop
244 class << self
245 remove_method :new
246 end
247 end
248 end
249 Yup.instantiate_all
250
251
252 puts "\n\n","Customized marshalling"
253 class A
254 include Singleton
255 attr_accessor :persist, :die
256 def _dump(depth)
257 # this strips the @die information from the instance
258 Marshal.dump(@persist,depth)
259 end
260 end
261
262 def A._load(str)
263 instance.persist = Marshal.load(str)
264 instance
265 end
266
267 a = A.instance
268 a.persist = ["persist"]
269 a.die = "die"
270 a.taint
271
272 stored_state = Marshal.dump(a)
273 # change state
274 a.persist = nil
275 a.die = nil
276 b = Marshal.load(stored_state)
277 p a == b # => true
278 p a.persist # => ["persist"]
279 p a.die # => nil
280
281
282 puts "\n\nSingleton with overridden default #inherited() hook"
283 class Up
284 end
285 def Up.inherited(sub_klass)
286 puts "#{sub_klass} subclasses #{self}"
287 end
288
289
290 class Middle < Up
291 include Singleton
292 end
293
294 class Down < Middle; end
295
296 puts "and basic \"Down test\" is #{Down.instance == Down.instance}\n
d926965 Update stdlib to 1.8.7
Evan Phoenix authored
297 Various exceptions"
8f59330 Brian Shirai Added 1.8.6 stable stdlib.
brixen authored
298
299 begin
300 module AModule
301 include Singleton
302 end
303 rescue TypeError => mes
304 puts mes #=> Inclusion of the OO-Singleton module in module AModule
305 end
306
307 begin
308 'aString'.extend Singleton
309 rescue NoMethodError => mes
310 puts mes #=> undefined method `extend_object' for Singleton:Module
311 end
312
313 end
Something went wrong with that request. Please try again.