PageRenderTime 34ms CodeModel.GetById 1ms app.highlight 29ms RepoModel.GetById 2ms app.codeStats 0ms

/IronPython_Main/External.LCA_RESTRICTED/Languages/Ruby/redist-libs/ruby/1.9.1/singleton.rb

#
Ruby | 313 lines | 187 code | 46 blank | 80 comment | 7 complexity | b9e3aa2b64235f69f12ddc63575a756d MD5 | raw file
  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#
 12#      a,b  = Klass.instance, Klass.instance
 13#      a == b    # => true
 14#      Klass.new #  NoMethodError - new is private ...
 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
 33# *  Klass.inherited(sub_klass) and Klass.clone()  -
 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()
 42#         return @singleton__instance__
 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
 63require 'thread'
 64
 65module 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
 73
 74  #  default marshalling strategy
 75  def _dump(depth = -1)
 76    ''
 77  end
 78
 79  module SingletonClassMethods
 80    # properly clone the Singleton pattern - did you know
 81    # that duping doesn't copy class methods?
 82    def clone
 83      Singleton.__init__(super)
 84    end
 85
 86    def _load(str)
 87      instance
 88    end
 89
 90    private
 91
 92    #  ensure that the Singleton pattern is properly inherited
 93    def inherited(sub_klass)
 94      super
 95      Singleton.__init__(sub_klass)
 96    end
 97  end
 98
 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__
112      end
113      klass
114    end
115
116    private
117
118    #  extending an object with Singleton is a bad idea
119    undef_method :extend_object
120
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
128
129    def included(klass)
130      super
131      klass.private_class_method  :new, :allocate
132      klass.extend SingletonClassMethods
133      Singleton.__init__(klass)
134    end
135  end
136
137end
138
139
140if __FILE__ == $0
141
142def num_of_instances(klass)
143    "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
144end
145
146# The basic and most important example.
147
148class SomeSingletonClass
149  include Singleton
150end
151puts "There are #{num_of_instances(SomeSingletonClass)}"
152
153a = SomeSingletonClass.instance
154b = SomeSingletonClass.instance # a and b are same object
155puts "basic test is #{a == b}"
156
157begin
158  SomeSingletonClass.new
159rescue  NoMethodError => mes
160  puts mes
161end
162
163
164
165puts "\nThreaded example with exception and customized #_instantiate?() hook"; p
166Thread.abort_on_exception = false
167
168class Ups < SomeSingletonClass
169  def initialize
170    self.class.__sleep
171    puts "initialize called by thread ##{Thread.current[:i]}"
172  end
173end
174
175class << Ups
176  def _instantiate?
177    @enter.push Thread.current[:i]
178    while false.equal?(@singleton__instance__)
179      @singleton__mutex__.unlock
180      sleep 0.08
181      @singleton__mutex__.lock
182    end
183    @leave.push Thread.current[:i]
184    @singleton__instance__
185  end
186
187  def __sleep
188    sleep(rand(0.08))
189  end
190
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
202
203  def instantiate_all
204    @enter = []
205    @leave = []
206    1.upto(9) {|i|
207      Thread.new {
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
223end
224
225
226Ups.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
236puts "\nLets see if class level cloning really works"
237Yup = Ups.clone
238def 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
248end
249Yup.instantiate_all
250
251
252puts "\n\n","Customized marshalling"
253class 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
260end
261
262def A._load(str)
263  instance.persist = Marshal.load(str)
264  instance
265end
266
267a = A.instance
268a.persist = ["persist"]
269a.die = "die"
270a.taint
271
272stored_state = Marshal.dump(a)
273# change state
274a.persist = nil
275a.die = nil
276b = Marshal.load(stored_state)
277p a == b  #  => true
278p a.persist  #  => ["persist"]
279p a.die      #  => nil
280
281
282puts "\n\nSingleton with overridden default #inherited() hook"
283class Up
284end
285def Up.inherited(sub_klass)
286  puts "#{sub_klass} subclasses #{self}"
287end
288
289
290class Middle < Up
291  include Singleton
292end
293
294class Down < Middle; end
295
296puts  "and basic \"Down test\" is #{Down.instance == Down.instance}\n
297Various exceptions"
298
299begin
300  module AModule
301    include Singleton
302  end
303rescue TypeError => mes
304  puts mes  #=> Inclusion of the OO-Singleton module in module AModule
305end
306
307begin
308  'aString'.extend Singleton
309rescue NoMethodError => mes
310  puts mes  #=> undefined method `extend_object' for Singleton:Module
311end
312
313end