From 86298123d748030ec9dcbb64ba3bf86816de9de7 Mon Sep 17 00:00:00 2001 From: matz Date: Tue, 29 Jan 2002 07:16:09 +0000 Subject: * file.c (rb_stat_rdev_major): added. [new] * file.c (rb_stat_rdev_minor): added. [new] * file.c (rb_stat_inspect): print mode in octal. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@2027 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 8 ++ file.c | 40 ++++++- lib/open3.rb | 1 + lib/singleton.rb | 327 ++++++++++++++++++++++++++++++++++++++----------------- misc/inf-ruby.el | 11 +- 5 files changed, 283 insertions(+), 104 deletions(-) diff --git a/ChangeLog b/ChangeLog index c3d7df3912..33a3e0e177 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,14 @@ Mon Jan 28 18:33:18 2002 Nobuyoshi Nakada * parse.y (yylex): strict check for numbers. +Mon Jan 28 18:01:01 2002 Yukihiro Matsumoto + + * file.c (rb_stat_rdev_major): added. [new] + + * file.c (rb_stat_rdev_minor): added. [new] + + * file.c (rb_stat_inspect): print mode in octal. + Mon Jan 28 13:29:41 2002 K.Kosako * eval.c (is_defined): defined?(Foo::Baz) should check constants diff --git a/file.c b/file.c index e3bc81cdd8..8d6d5e0ca0 100644 --- a/file.c +++ b/file.c @@ -210,6 +210,30 @@ rb_stat_rdev(self) #endif } +static VALUE +rb_stat_rdev_major(self) + VALUE self; +{ +#if defined(HAVE_ST_RDEV) && defined(major) + long rdev = get_stat(self)->st_rdev; + return ULONG2NUM(major(rdev)); +#else + return INT2FIX(0); +#endif +} + +static VALUE +rb_stat_rdev_minor(self) + VALUE self; +{ +#if defined(HAVE_ST_RDEV) && defined(minor) + long rdev = get_stat(self)->st_rdev; + return ULONG2NUM(minor(rdev)); +#else + return INT2FIX(0); +#endif +} + static VALUE rb_stat_size(self) VALUE self; @@ -290,15 +314,23 @@ rb_stat_inspect(self) rb_str_buf_cat2(str, " "); for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) { - VALUE str2; + VALUE v; if (i > 0) { rb_str_buf_cat2(str, ", "); } rb_str_buf_cat2(str, member[i].name); rb_str_buf_cat2(str, "="); - str2 = rb_inspect((*member[i].func)(self)); - rb_str_append(str, str2); + v = (*member[i].func)(self); + if (i == 2) { /* mode */ + char buf[32]; + + sprintf(buf, "0%o", NUM2INT(v)); + rb_str_buf_cat2(str, buf); + } + else { + rb_str_append(str, rb_inspect(v)); + } } rb_str_buf_cat2(str, ">"); OBJ_INFECT(str, self); @@ -2553,6 +2585,8 @@ Init_File() rb_define_method(rb_cStat, "uid", rb_stat_uid, 0); rb_define_method(rb_cStat, "gid", rb_stat_gid, 0); rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0); + rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0); + rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0); rb_define_method(rb_cStat, "size", rb_stat_size, 0); rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0); rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0); diff --git a/lib/open3.rb b/lib/open3.rb index 33701bbfc0..a6e6c5d62b 100644 --- a/lib/open3.rb +++ b/lib/open3.rb @@ -40,6 +40,7 @@ module Open3 pe[1].close Process.waitpid(pid) pi = [pw[1], pr[0], pe[0]] + pw[1].sync = true if defined? yield begin return yield(*pi) diff --git a/lib/singleton.rb b/lib/singleton.rb index 5a9b271fbd..2b5a3a3cb5 100644 --- a/lib/singleton.rb +++ b/lib/singleton.rb @@ -1,130 +1,203 @@ -# The Singleton module implements the Singleton pattern - i.e. +# The Singleton module implements the Singleton pattern. # -# class Klass -# include Singleton -# # ... -# end +# Usage: +# class Klass +# include Singleton +# # ... +# end # -# * ensures that only one instance of Klass called ``the instance'' -# can be created. +# * this ensures that only one instance of Klass lets call it +# ``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(). +# * ``The instance'' is created at instanciation time, in other words +# the first call of Klass.instance(), thus # # class OtherKlass # include Singleton # # ... # end -# p "#{ObjectSpace.each_object(OtherKlass) {}}" # => 0 +# ObjectSpace.each_object(OtherKlass){} # => 0. # -# * This behavior is preserved under inheritance. +# * This behavior is preserved under inheritance and cloning. # # -# This achieved by marking -# * Klass.new and Klass.allocate - as private and modifying -# * Klass.inherited(sub_klass) - to ensure -# that the Singleton pattern is properly inherited. +# This is achieved by marking +# * Klass.new and Klass.allocate - as private +# * removing #clone and #dup and modifying +# * Klass.inherited(sub_klass) and Klass.clone() - +# to ensure that the Singleton pattern is properly +# inherited and cloned. # -# 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. +# In addition Klass is providing the additional class methods +# * Klass.instance() - returning ``the instance''. After a successful +# self modifying instanciating first call the method body is a simple +# def Klass.instance() +# return @__instance__ +# end +# * Klass._load(str) - calls 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 instanciating thread. # # 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 custom -# _dump(depth) and _load(str) method allows the (partial) resurrection -# of a previous state of ``the instance'' - see third example. -# +# * _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''. module Singleton - def Singleton.included (klass) - # should this be checked? - # raise TypeError.new "..." if klass.type == Module - klass.module_eval { - undef_method :clone - undef_method :dup - } - 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 - sub_klass.instance_eval { @__instance__ = nil } - def sub_klass.instance - unless @__instance__.nil? - # is the extra flexiblity having the hook method - # _wait() around ever useful? - _wait() - # 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) while false.equal?(@__instance__) - end - private :new, :allocate - # hook methods are also marked private - private :_load,:_wait + private + # default marshalling strategy + def _dump(depth=-1) '' end + + class << self + # extending an object with Singleton is a bad idea + undef_method :extend_object + private + def append_features(mod) + # This catches ill advisted inclusions of Singleton in + # singletons types (sounds like an oxymoron) and + # helps out people counting on transitive mixins + unless mod.instance_of? (Class) + raise TypeError.new "Inclusion of the OO-Singleton module in module #{mod}" + end + unless (class << mod; self end) <= (class << Object; self end) + raise TypeError.new "Inclusion of the OO-Singleton module in singleton type" + end + super + end + def included (klass) + # remove build in copying methods + klass.class_eval do + undef_method(:clone) rescue nil + undef_method(:dup) rescue nil + end + + # initialize the ``klass instance variable'' @__instance__ to nil + klass.instance_eval do @__instance__ = nil end + class << klass + # a main point of the whole exercise - make + # new and allocate private + private :new, :allocate + + # declare the self modifying klass#instance method + define_method (:instance, Singleton::FirstInstanceCall) + + # simple waiting loop hook - should do in most cases + # note the pre/post-conditions of a thread-critical state + private + def _instanciate?() + while false.equal?(@__instance__) + Thread.critical = false + sleep(0.08) + Thread.critical = true + end + @__instance__ + end + + # default Marshalling strategy + def _load(str) instance end + + # ensure that the Singleton pattern is properly inherited + def inherited(sub_klass) + super + sub_klass.instance_eval do @__instance__ = nil end + class << sub_klass + define_method (:instance, Singleton::FirstInstanceCall) + end + end + + public + # properly clone the Singleton pattern. Question - Did + # you know that duping doesn't copy class methods? + def clone + res = super + res.instance_eval do @__instance__ = nil end + class << res + define_method (:instance, Singleton::FirstInstanceCall) + end + res + end + end # of << klass + end # of included + end # of << Singleton + + 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 + @__instance__ = new + ensure + if @__instance__ + def self.instance() @__instance__ end + else + @__instance__ = nil # failed instance creation + end + end + elsif _instanciate?() + Thread.critical = false + else + @__instance__ = false + Thread.critical = false + begin + @__instance__ = new + ensure + if @__instance__ + def self.instance() @__instance__ end + else + @__instance__ = nil + end + end + end + @__instance__ end - klass.inherited klass - end - private - def _dump(depth) - return "" - end end + + + if __FILE__ == $0 -#basic example +def num_of_instances(klass) + "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)" +end + +# The basic and most important example. The latter examples demonstrate +# advanced features that have no relevance for the general usage + class SomeSingletonClass include Singleton end +puts "There are #{num_of_instances(SomeSingletonClass)}" + a = SomeSingletonClass.instance b = SomeSingletonClass.instance # a and b are same object -p a == b # => true +puts "basic test is #{a == b}" + begin SomeSingletonClass.new rescue NoMethodError => mes puts mes end -# threaded example with exception and customized hook #_wait method + + +puts "\nThreaded example with exception and customized #_instanciate?() hook"; p Thread.abort_on_exception = false -def num_of_instances(mod) - "#{ObjectSpace.each_object(mod){}} #{mod} instance" -end class Ups < SomeSingletonClass def initialize @@ -132,18 +205,23 @@ class Ups < SomeSingletonClass puts "initialize called by thread ##{Thread.current[:i]}" end class << self - def _wait + def _instanciate? @enter.push Thread.current[:i] - sleep 0.02 while false.equal?(@__instance__) + while false.equal?(@__instance__) + Thread.critical = false + sleep 0.04 + Thread.critical = true + end @leave.push Thread.current[:i] + @__instance__ end def __sleep - sleep (rand(0.1)) + sleep (rand(0.08)) end def allocate __sleep def self.allocate; __sleep; super() end - raise "allocation in thread ##{Thread.current[:i]} aborted" + raise "boom - allocation in thread ##{Thread.current[:i]} aborted" end def instanciate_all @enter = [] @@ -159,9 +237,9 @@ class Ups < SomeSingletonClass end end end - puts "Before there were #{num_of_instances(Ups)}s" - sleep 3 - puts "Now there is #{num_of_instances(Ups)}" + puts "Before there were #{num_of_instances(self)}" + sleep 5 + 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" end @@ -177,8 +255,17 @@ Ups.instanciate_all # 2; 3; 6; 1; 7; 5; 9; 4 was the order of threads entering the waiting loop # 3; 2; 1; 7; 6; 5; 4; 9 was the order of threads leaving the waiting loop +puts "\nLets see if class level cloning really works" +Yup = Ups.clone +def Yup.allocate + __sleep + def self.allocate; __sleep; super() end + raise "boom - allocation in thread ##{Thread.current[:i]} aborted" +end +Yup.instanciate_all + -# Customized marshalling +puts "\n","Customized marshalling" class A include Singleton attr_accessor :persist, :die @@ -195,6 +282,7 @@ end a = A.instance a.persist = ["persist"] a.die = "die" +a.taint stored_state = Marshal.dump(a) # change state @@ -203,6 +291,47 @@ a.die = nil b = Marshal.load(stored_state) p a == b # => true p a.persist # => ["persist"] -p a.die # => nil +p a.die # => nil + +puts "\n\nSingleton with overridden default #inherited() hook" +class Up + def Up.inherited(sub_klass) + puts "#{sub_klass} subclasses #{self}" + end +end + + +class Middle < Up + undef_method :dup + include Singleton +end +class Down < Middle; end + +puts "basic test is #{Down.instance == Down.instance}" + + +puts "\n","Various exceptions" + +begin + module AModule + include Singleton + end +rescue TypeError => mes + puts mes #=> Inclusion of the OO-Singleton module in module AModule +end + +begin + class << 'aString' + include Singleton + end +rescue TypeError => mes + puts mes # => Inclusion of the OO-Singleton module in singleton type +end + +begin + 'aString'.extend Singleton +rescue NoMethodError => mes + puts mes #=> undefined method `extend_object' for Singleton:Module +end end diff --git a/misc/inf-ruby.el b/misc/inf-ruby.el index dab2d51743..7679ff91d7 100644 --- a/misc/inf-ruby.el +++ b/misc/inf-ruby.el @@ -15,7 +15,7 @@ ;;; for example : ;;; ;;; (autoload 'ruby-mode "ruby-mode" -;;; "Mode for editing ruby source files") +;;; "Mode for editing ruby source files" t) ;;; (setq auto-mode-alist ;;; (append '(("\\.rb$" . ruby-mode)) auto-mode-alist)) ;;; (setq interpreter-mode-alist (append '(("ruby" . ruby-mode)) @@ -35,9 +35,16 @@ ;;; HISTORY ;;; senda - 8 Apr 1998: Created. ;;; $Log$ +;;; Revision 1.4 2002/01/29 07:16:09 matz +;;; * file.c (rb_stat_rdev_major): added. [new] +;;; +;;; * file.c (rb_stat_rdev_minor): added. [new] +;;; +;;; * file.c (rb_stat_inspect): print mode in octal. +;;; ;;; Revision 1.3 1999/12/01 09:24:18 matz ;;; 19991201 -;;; +;;; ;;; Revision 1.2 1999/08/13 05:45:18 matz ;;; 1.4.0 ;;; -- cgit v1.2.3