Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Nested scopes and the Singleton pattern

Reply
Thread Tools

Nested scopes and the Singleton pattern

 
 
Hal Fulton
Guest
Posts: n/a
 
      01-14-2004
Hello all,

This is really two questions.

I've been putting together a nested class structure in pieces
since it is a little large and unwieldy.

Here's a snippet of something I've run across.

By the way, my last change was to implement a simple version of the
Singleton pattern (without using singleton.rb).


# Note: *Reopening* existing classes

class ABC
class XYZ
@@instance = nil
def initialize(foo)
# Do some stuff...
# and then:
def XYZ.new(foo)
@@instance
end
end
end
end


This works fine.

First question: Is this a valid way to implement Singleton, or am I
overlooking something?


Then I thought: Well, let's get rid of the unnecessary verbiage.
(Remember, I'm REOPENING these classes.)

So I did:

# Note: *Reopening* existing classes

class ABC::XYZ
@@instance = nil
def initialize(foo)
# Do some stuff...
# and then:
def ABC::XYZ.new(foo)
@@instance
end
end
end

But it didn't work.

This does work:

# Note: *Reopening* existing classes

class ABC::XYZ
@@instance = nil
def initialize(foo)
# Do some stuff...
# and then:
klass = ABC::XYZ
def klass.new(foo)
@@instance
end
end
end


Second question: Why should these two be different?


Thanks,
Hal Fulton



 
Reply With Quote
 
 
 
 
nobu.nokada@softhome.net
Guest
Posts: n/a
 
      01-15-2004
Hi,

At Thu, 15 Jan 2004 06:40:07 +0900,
Hal Fulton wrote:
> By the way, my last change was to implement a simple version of the
> Singleton pattern (without using singleton.rb).
>
>
> # Note: *Reopening* existing classes
>
> class ABC
> class XYZ
> @@instance = nil
> def initialize(foo)
> # Do some stuff...
> # and then:
> def XYZ.new(foo)
> @@instance
> end
> end
> end
> end
>
>
> This works fine.
>
> First question: Is this a valid way to implement Singleton, or am I
> overlooking something?


Have you considered about race conditions?

> So I did:
>
> # Note: *Reopening* existing classes
>
> class ABC::XYZ
> @@instance = nil
> def initialize(foo)
> # Do some stuff...
> # and then:
> def ABC::XYZ.new(foo)
> @@instance
> end
> end
> end
>
> But it didn't work.


def ABC::XYZ means singleton method XYZ of ABC by itself, so
trailing . is superfluous. Try

def (ABC::XYZ).new(foo)

--
Nobu Nakada


 
Reply With Quote
 
 
 
 
Hal Fulton
Guest
Posts: n/a
 
      01-15-2004
http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:

>>First question: Is this a valid way to implement Singleton, or am I
>>overlooking something?

>
> Have you considered about race conditions?
>


Thank you, Nobu. No, I did not consider race conditions.

I suppose singleton.rb is thread safe?

> def ABC::XYZ means singleton method XYZ of ABC by itself, so
> trailing . is superfluous. Try
>
> def (ABC::XYZ).new(foo)


Ahh, that is clear. But I could not see it before.


Thanks very much,
Hal




 
Reply With Quote
 
nobu.nokada@softhome.net
Guest
Posts: n/a
 
      01-15-2004
Hi,

At Thu, 15 Jan 2004 13:24:19 +0900,
Hal Fulton wrote:
> >>First question: Is this a valid way to implement Singleton, or am I
> >>overlooking something?

> >
> > Have you considered about race conditions?

>
> Thank you, Nobu. No, I did not consider race conditions.
>
> I suppose singleton.rb is thread safe?


Just using Thread.critical. And seems close to your idea
excepting for it uses "instance" method rather than "new".

--
Nobu Nakada


 
Reply With Quote
 
Hal Fulton
Guest
Posts: n/a
 
      01-15-2004
(E-Mail Removed) wrote:
> Hi,
>
> At Thu, 15 Jan 2004 13:24:19 +0900,
> Hal Fulton wrote:
>
>>>>First question: Is this a valid way to implement Singleton, or am I
>>>>overlooking something?

>>
>> >
>> > Have you considered about race conditions?

>>
>>Thank you, Nobu. No, I did not consider race conditions.
>>
>>I suppose singleton.rb is thread safe?

>
>
> Just using Thread.critical. And seems close to your idea
> excepting for it uses "instance" method rather than "new".


Actually I like "new" instead of "instance". I guess this
(instance) is to emphasize that there can only be one. But
I prefer simply calling new as usual, which was why I did this.

Hal




 
Reply With Quote
 
nobu.nokada@softhome.net
Guest
Posts: n/a
 
      01-15-2004
Hi,

At Thu, 15 Jan 2004 13:55:44 +0900,
Hal Fulton wrote:
> >>I suppose singleton.rb is thread safe?

> >
> > Just using Thread.critical. And seems close to your idea
> > excepting for it uses "instance" method rather than "new".


Not accurate, it uses 3-state; before, during and after
creation, but I feel it should use mutex or something.

> Actually I like "new" instead of "instance". I guess this
> (instance) is to emphasize that there can only be one. But
> I prefer simply calling new as usual, which was why I did this.


singleton.rb is for generic use, so it gets rid of overriding
instance methods, I guess.

--
Nobu Nakada

 
Reply With Quote
 
Paul Brannan
Guest
Posts: n/a
 
      01-15-2004
On Thu, Jan 15, 2004 at 06:39:13PM +0900, (E-Mail Removed) wrote:
> At Thu, 15 Jan 2004 13:55:44 +0900,
> Hal Fulton wrote:
> > > Just using Thread.critical. And seems close to your idea
> > > excepting for it uses "instance" method rather than "new".

>
> Not accurate, it uses 3-state; before, during and after
> creation, but I feel it should use mutex or something.


I think I agree. It took me an hour to understand the Singleton code
last time I read it, and when I look at it again now, I seem to have
forgotten how it works.

It seems like using a Mutex plus Thread.exclusive around the code that
redefines instance() should be sufficient, but perhaps I'm missing
something.

Paul



 
Reply With Quote
 
nobu.nokada@softhome.net
Guest
Posts: n/a
 
      01-16-2004
Hi,

At Fri, 16 Jan 2004 07:46:39 +0900,
Christoph wrote:
> > > > Just using Thread.critical. And seems close to your idea
> > > > excepting for it uses "instance" method rather than "new".

> >
> > Not accurate, it uses 3-state; before, during and after
> > creation, but I feel it should use mutex or something.

>
> You probably always need a 3-state if you want a self
> modifying first instance call (at least in some implicit way).
>
> Here is a mutexy version of singleton.rb I wrote some time
> ago - it is probably more robust then the current code but equally
> obscure - sorry ...


Due to the line ending codes, your patch contains whole files.
Following is a bit modified version.


Index: lib/singleton.rb
================================================== =================
RCS file: /cvs/ruby/src/ruby/lib/singleton.rb,v
retrieving revision 1.21
diff -u -2 -p -d -r1.21 singleton.rb
--- lib/singleton.rb 22 Aug 2003 08:09:58 -0000 1.21
+++ lib/singleton.rb 16 Jan 2004 01:26:06 -0000
@@ -12,5 +12,5 @@
# a,b = Klass.instance, Klass.instance
# a == b # => true
-# a.new # NoMethodError - new is private ...
+# Klass.new # NoMethodError - new is private ...
#
# * ``The instance'' is created at instanciation time, in other
@@ -31,7 +31,5 @@
#
# Providing (or modifying) the class methods
-# * Klass.inherited(sub_klass) and Klass.clone() -
-# to ensure that the Singleton pattern is properly
-# inherited and cloned.
+# * Klass.inherited(sub_klass) and Klass.initialize_copy()
#
# * Klass.instance() - returning ``the instance''. After a
@@ -45,20 +43,12 @@
# * 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
+# * clone and dup - raising TypeErrors to prevent cloning
#
-# * _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''.
-
+# * _dump(depth) - returning the empty string, in other words
+# marshalling strips all state information. Providing custom
+# _load(str) and _dump(depth) hooks allows the (partially)
+# resurrections of a previous state of ``the instance''.


@@ -82,60 +72,61 @@ 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
+ FirstInstanceCall = proc do ||
+ critical, Thread.critical = Thread.critical, true
+ begin
+ if instanciating = @__instance__.nil?
+ @__instance__ = false
+ Thread.critical = critical
@__instance__ = new
- ensure
- if @__instance__
- class <<self
- remove_method :instance
- def instance; @__instance__ end
- end
- else
- @__instance__ = nil # failed instance creation
- end
+ elsif !@__instance__
+ @__instanciating_queue__ << Thread.current
+ Thread.stop
+ Thread.critical = true
+ retry
end
- elsif _instanciate?()
- Thread.critical = false
- else
- @__instance__ = false
- Thread.critical = false
- begin
- @__instance__ = new
- ensure
+ @__instance__
+ ensure
+ if instanciating
+ Thread.critical= true
if @__instance__
- class <<self
+ class << self
remove_method :instance
def instance; @__instance__ end
end
+ remove_instance_variable(@__instanciating_queue__) .each do |thr|
+ thr.wakeup
+ end
else
@__instance__ = nil
+ if thr = @__instanciating_queue__.shift
+ thr.wakeup
+ end
end
end
+ Thread.critical = critical
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)
+ module SingletonClassMethods
+ def self.extended(klass)
+ klass.instance_eval {
+ @__instance__ = nil
+ @__instanciating_queue__ = []
+ }
+ class << klass
+ define_method(:instance, FirstInstanceCall)
+ end
end

private

- # ensure that the Singleton pattern is properly inherited
+ def initialize_copy(orig)
+ super
+ SingletonClassMethods.extended(self)
+ end
+
def inherited(sub_klass)
super
- Singleton.__init__(sub_klass)
+ SingletonClassMethods.extended(sub_klass)
end

@@ -143,23 +134,6 @@ class << Singleton
instance
end
-
- # waiting-loop hook
- def _instanciate?()
- while false.equal?(@__instance__)
- Thread.critical = false
- sleep(0.0 # 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
@@ -177,7 +151,7 @@ class << Singleton
def included(klass)
super
- klass.private_class_method :new, :allocate
+ klass.private_class_method :new,:allocate
klass.extend SingletonClassMethods
- Singleton.__init__(klass)
+ SingletonClassMethods.extended(klass)
end
end
@@ -186,9 +160,4 @@ end

if __FILE__ == $0
-
-def num_of_instances(klass)
- "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
-end
-
# The basic and most important example.

@@ -196,5 +165,7 @@ class SomeSingletonClass
include Singleton
end
-puts "There are #{num_of_instances(SomeSingletonClass)}"
+
+num = ObjectSpace.each_object(SomeSingletonClass) {}
+puts "There are #{num} of SomeSingletonClass instances"

a = SomeSingletonClass.instance
@@ -209,90 +180,81 @@ end


+puts "\nThreaded example with exception"
+$stdout.sync= true

-puts "\nThreaded example with exception and customized #_instanciate?() hook"; p
-Thread.abort_on_exception = false
+class Foo < SomeSingletonClass
+ @attempts = 0

-class Ups < SomeSingletonClass
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
+ @valid = false
+ sleep(rand(0.1))
+ if self.class.attempts < 3
+ raise "boom - initialize failed for thread ##{Thread.current[:i]}"
+ else
+ @valid = true
+ puts "yes! - initialize succceeded for thread ##{Thread.current[:i]}"
end
- @leave.push Thread.current[:i]
- @__instance__
+ ensure
+ self.class.attempts+= 1
end

- def __sleep
- sleep(rand(0.0)
- end
+ def valid?
+ @valid
+ end
+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
+class << Foo
+ attr_accessor :attempts

def instanciate_all
- @enter = []
- @leave = []
- 1.upto(9) {|i|
- Thread.new {
+ Thread.current.priority = 101
+ thrs= Array.new(101) {|i|
+ curr= Thread.new {
begin
+ sleep(rand(0.1))
Thread.current[:i] = i
- __sleep
instance
- rescue RuntimeError => mes
+ rescue => mes
puts mes
end
}
+ curr.priority = rand(101)
+ curr
}
- puts "Before there were #{num_of_instances(self)}"
+ puts "Before there existed #{num} valid #{self} instance(s)"
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"
+ thrs.each {|t| t.join }
+ puts "Now there exist(s) #{num} valid #{self} instance(s)"
end
-end
+
+ def num
+ cnt = 0
+ ObjectSpace.each_object(self) {|o| cnt+=1 if o.valid? }
+ cnt
+ end
+
+ private
+
+ def initialize_copy(orig)
+ super
+ @attempts = 0
+ end
+end


-Ups.instanciate_all
-# 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
+Foo.instanciate_all
+# results in something like:
+# Before there were 0 valid Foo instance(s)
+# boom - initialize failed for thread #78
+# boom - initialize failed for thread #93
+# boom - initialize failed for thread #8
+# yes - initialize succceeded for thread #27
+# Now there are 1 valid Foo instance(s)
+# Now there are 1 valid Foo instance(s)


puts "\nLets see if class level cloning really works"
-Yup = Ups.clone
-def Yup.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
-Yup.instanciate_all
+Baz = Foo.clone
+Baz.instanciate_all




--
Nobu Nakada


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Singleton classes and Singleton pattern Wilhelm Ruby 1 10-11-2006 01:08 PM
Nested scopes, and augmented assignment Tim N. van der Leeuw Python 39 07-10-2006 03:05 PM
Nested scopes and class variables Dave Benjamin Python 7 02-03-2005 07:42 PM
Trouble with nested scopes when trying to rebind a variable Fernando Rodriguez Python 2 11-21-2003 03:40 PM
Question about nested scopes Miki Tebeka Python 3 10-09-2003 07:27 AM



Advertisments