From 186c8b592a8b5badfd42ff7295c5edb44ef1abdd Mon Sep 17 00:00:00 2001 From: matz Date: Tue, 27 Nov 2001 10:00:35 +0000 Subject: * marshal.c (w_float): must distinguish -0.0 from 0.0. * gc.c (gc_mark_all): tweak mark order for little bit better scan. * gc.c (rb_gc_mark): ditto. * gc.c (rb_gc): ditto. * enum.c (sort_by_i): slight performance boost. * gc.c (gc_mark_rest): should call gc_mark_children(), not gc_mark(). * gc.c (rb_gc_mark): may cause infinite looop. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1861 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 22 +++++ array.c | 24 +++++- enum.c | 24 +++++- eval.c | 15 ++++ ext/pty/extconf.rb | 2 +- ext/pty/pty.c | 10 +-- gc.c | 53 ++++++------ lib/singleton.rb | 230 +++++++++++++++++++++++++++++++++++++++++++---------- marshal.c | 4 + 9 files changed, 301 insertions(+), 83 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3159db3e4b..3c3b48e0d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,20 @@ +Tue Nov 27 02:15:25 2001 Yukihiro Matsumoto + + * marshal.c (w_float): must distinguish -0.0 from 0.0. + Mon Nov 26 20:57:24 2001 Akinori MUSHA * ext/Setup*, ext/syslog/*: import the "syslog" module from the rough ruby project. +Mon Nov 26 16:14:42 2001 K.Kosako + + * gc.c (gc_mark_all): tweak mark order for little bit better scan. + + * gc.c (rb_gc_mark): ditto. + + * gc.c (rb_gc): ditto. + Mon Nov 26 16:54:59 2001 Usaku Nakamura * win32/win32.c (mypopen): fixed that mypclose() didn't really close @@ -11,11 +23,21 @@ Mon Nov 26 16:54:59 2001 Usaku Nakamura * win32/win32.c (CreateChild): set STARTF_USESTDHANDLES flag only when some handles are passed. +Mon Nov 26 16:31:28 2001 Yukihiro Matsumoto + + * enum.c (sort_by_i): slight performance boost. + Sun Nov 25 21:02:18 2001 Usaku Nakamura * parse.y (str_extend): change types of second and third arguments from char to int. +Thu Nov 22 20:15:28 2001 TAMURA Takashi + + * gc.c (gc_mark_rest): should call gc_mark_children(), not gc_mark(). + + * gc.c (rb_gc_mark): may cause infinite looop. + Thu Nov 22 00:28:13 2001 Yukihiro Matsumoto * parse.y (str_extend): should check nesting parentheses in #{}. diff --git a/array.c b/array.c index 9d5dcd768f..6a419d800e 100644 --- a/array.c +++ b/array.c @@ -1086,6 +1086,26 @@ rb_ary_sort(ary) return ary; } +static VALUE +sort_inplace(ary) + VALUE ary; +{ + qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE),sort_2); + return ary; +} + +VALUE +rb_ary_sort_inplace(ary) + VALUE ary; +{ + rb_ary_modify(ary); + if (RARRAY(ary)->len <= 1) return ary; + + FL_SET(ary, ARY_TMPLOCK); /* prohibit modification during sort */ + rb_ensure(sort_inplace, ary, sort_unlock, ary); + return ary; +} + static VALUE rb_ary_collect(ary) VALUE ary; @@ -1493,7 +1513,9 @@ rb_ary_cmp(ary, ary2) { long i, len; - ary2 = to_ary(ary2); + if (TYPE(ary2) != T_ARRAY) { + ary2 = to_ary(ary2); + } len = RARRAY(ary)->len; if (len > RARRAY(ary2)->len) { len = RARRAY(ary2)->len; diff --git a/enum.c b/enum.c index af01645220..fb72849346 100644 --- a/enum.c +++ b/enum.c @@ -208,7 +208,23 @@ sort_by_i(i, memo) VALUE i; NODE *memo; { - VALUE e = rb_ary_new3(3, rb_yield(i), INT2NUM(memo->u3.cnt), i); + VALUE v, e; + + v = rb_yield(i); + if (TYPE(v) == T_ARRAY) { + int j, len = RARRAY(v)->len; + + e = rb_ary_new2(len+2); + for (j=0; jptr[j] = RARRAY(v)->ptr[j]; + } + RARRAY(e)->ptr[j++] = INT2NUM(memo->u3.cnt); + RARRAY(e)->ptr[j] = i; + RARRAY(e)->len = len + 2; + } + else { + e = rb_ary_new3(3, v, INT2NUM(memo->u3.cnt), i); + } rb_ary_push(memo->u1.value, e); memo->u3.cnt++; return Qnil; @@ -225,16 +241,16 @@ static VALUE enum_sort_by(obj) VALUE obj; { - VALUE ary = rb_ary_new(); + VALUE ary = rb_ary_new2(2000); NODE *memo = rb_node_newnode(NODE_MEMO, ary, 0, 0); long i; rb_iterate(rb_each, obj, sort_by_i, (VALUE)memo); rb_gc_force_recycle((VALUE)memo); - rb_iterate(rb_ary_sort_bang, ary, sort_by_sort_body, 0); + rb_ary_sort_inplace(ary); for (i=0; ilen; i++) { VALUE e = RARRAY(ary)->ptr[i]; - RARRAY(ary)->ptr[i] = rb_ary_entry(e, 2); + RARRAY(ary)->ptr[i] = RARRAY(e)->ptr[2]; } return ary; diff --git a/eval.c b/eval.c index e54cf21da5..9cf2eb3759 100644 --- a/eval.c +++ b/eval.c @@ -7614,6 +7614,9 @@ rb_thread_schedule() copy_fds(&exceptfds, &th->exceptfds, th->fd); if (max < th->fd) max = th->fd; need_select = 1; + if (th->wait_for & WAIT_TIME) { + need_select = 2; + } th->select_value = 0; } if (th->wait_for & WAIT_TIME) { @@ -7673,6 +7676,18 @@ rb_thread_schedule() } END_FOREACH_FROM(curr, th); } + if (n == 0 && need_select == 2) { + if (now < 0.0) now = timeofday(); + FOREACH_THREAD_FROM(curr, th) { + if ((th->wait_for & (WAIT_SELECT|WAIT_TIME)) && th->delay < now) { + th->status = THREAD_RUNNABLE; + th->wait_for = 0; + th->select_value = 0; + found = 1; + } + } + END_FOREACH_FROM(curr, th); + } if (n > 0) { now = -1.0; /* Some descriptors are ready. diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb index 51a4bc7cd3..ec35879a33 100644 --- a/ext/pty/extconf.rb +++ b/ext/pty/extconf.rb @@ -3,12 +3,12 @@ require 'mkmf' if /mswin32|mingw/ !~ RUBY_PLATFORM have_header("sys/stropts.h") have_func("setresuid") - $CFLAGS << "-DHAVE_DEV_PTMX" if /cygwin/ === RUBY_PLATFORM have_header("libutil.h") have_header("pty.h") have_library("util", "openpty") if have_func("openpty") or have_func("_getpty") or + have_func("ptsname") or have_func("ioctl") create_makefile('pty') end diff --git a/ext/pty/pty.c b/ext/pty/pty.c index 1760ebf598..def44b6f41 100644 --- a/ext/pty/pty.c +++ b/ext/pty/pty.c @@ -349,7 +349,7 @@ getDevice(master,slave) int i,j; char MasterName[DEVICELEN]; -#ifdef HAVE_DEV_PTMX +#ifdef HAVE_PTSNAME char *pn; void (*s)(); @@ -364,7 +364,7 @@ getDevice(master,slave) if(unlockpt(i) != -1) { if((pn = ptsname(i)) != NULL) { if((j = open(pn, O_RDWR, 0)) != -1) { -#if defined I_PUSH +#if defined I_PUSH && !defined linux if(ioctl(j, I_PUSH, "ptem") != -1) { if(ioctl(j, I_PUSH, "ldterm") != -1) { #endif @@ -372,7 +372,7 @@ getDevice(master,slave) *slave = j; strcpy(SlaveName, pn); return; -#if defined I_PUSH +#if defined I_PUSH && !defined linux } } #endif @@ -385,10 +385,10 @@ getDevice(master,slave) rb_raise(rb_eRuntimeError, "Cannot get Master/Slave device"); #else for (p = deviceNo; *p != NULL; p++) { - sprintf(MasterName ,MasterDevice,*p); + sprintf(MasterName,MasterDevice,*p); if ((i = open(MasterName,O_RDWR,0)) >= 0) { *master = i; - sprintf(SlaveName ,SlaveDevice,*p); + sprintf(SlaveName,SlaveDevice,*p); if ((j = open(SlaveName,O_RDWR,0)) >= 0) { *slave = j; chown(SlaveName, getuid(), getgid()); diff --git a/gc.c b/gc.c index 2ff8c303ca..e73fad4089 100644 --- a/gc.c +++ b/gc.c @@ -427,26 +427,23 @@ init_mark_stack() #define MARK_STACK_EMPTY (mark_stack_ptr == mark_stack) -static int mark_all; - static void rb_gc_mark_children(VALUE ptr); + static void gc_mark_all() { RVALUE *p, *pend; int i; - mark_all = 0; - while(!mark_all){ - mark_all = 1; - for (i = 0; i < heaps_used; i++) { - p = heaps[i]; pend = p + heaps_limits[i]; - while (p < pend) { - if ((p->as.basic.flags & FL_MARK) && - (p->as.basic.flags != FL_MARK)) { - rb_gc_mark_children((VALUE)p); - } - p++; + + init_mark_stack(); + for (i = 0; i < heaps_used; i++) { + p = heaps[i]; pend = p + heaps_limits[i]; + while (p < pend) { + if ((p->as.basic.flags & FL_MARK) && + (p->as.basic.flags != FL_MARK)) { + rb_gc_mark_children((VALUE)p); } + p++; } } } @@ -464,7 +461,7 @@ gc_mark_rest() while(p != tmp_arry){ p--; - rb_gc_mark(*p); + rb_gc_mark_children(*p); } } @@ -565,32 +562,28 @@ void rb_gc_mark(ptr) VALUE ptr; { + int ret; register RVALUE *obj = RANY(ptr); if (rb_special_const_p(ptr)) return; /* special const not marked */ if (obj->as.basic.flags == 0) return; /* free cell */ if (obj->as.basic.flags & FL_MARK) return; /* already marked */ - if (!mark_stack_overflow){ - int ret; - CHECK_STACK(ret); - if (ret) { + obj->as.basic.flags |= FL_MARK; + + CHECK_STACK(ret); + if (ret) { + if (!mark_stack_overflow) { if (mark_stack_ptr - mark_stack < MARK_STACK_MAX) { *mark_stack_ptr = ptr; - mark_stack_ptr++; - return; - }else{ + mark_stack_ptr++; + } + else { mark_stack_overflow = 1; } } } - - obj->as.basic.flags |= FL_MARK; - - if (mark_stack_overflow){ - mark_all &= 0; - return; - }else{ + else { rb_gc_mark_children(ptr); } } @@ -1175,8 +1168,8 @@ rb_gc() while (!MARK_STACK_EMPTY){ if (mark_stack_overflow){ gc_mark_all(); - break; - }else{ + } + else { gc_mark_rest(); } } diff --git a/lib/singleton.rb b/lib/singleton.rb index 1945c4446b..696d37c0da 100644 --- a/lib/singleton.rb +++ b/lib/singleton.rb @@ -1,49 +1,195 @@ -# Singleton module that ensures only one object to be allocated. -# -# Usage: -# class SomeSingletonClass -# include Singleton -# #.... -# end -# a = SomeSingletonClass.instance -# b = SomeSingletonClass.instance # a and b are same object -# p [a,b] -# a = SomeSingletonClass.new # error (`new' is private) - +# The Singleton module implements the Singleton pattern - i.e. +# +# class Klass +# include Singleton +# # ... +# end +# +# * ensures that only one instance of Klass called ``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(). +# +# class OtherKlass +# include Singleton +# # ... +# end +# p "#{ObjectSpace.each_object(OtherKlass) {}}" # => 0 +# +# * This behavior is preserved under inheritance. +# +# +# This achieved by marking +# * Klass.new and Klass.allocate - as private and +# * Klass.inherited(sub_klass) - modifying to ensure +# that the Singleton pattern is properly inherited. +# +# 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. +# +# 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 costume +# _dump(depth) and _load(str) method allows the (partial) resurrection +# of a previous state of ``the instance'' - see third example. +# module Singleton - def Singleton.included(klass) - klass.private_class_method(:new) - klass.instance_eval %{ - @__instance__ = nil - def instance - if defined? @__allocating__ - until @__instance__ - sleep 0.5 - end - elsif ! @__instance__ - Thread.critical = true - @__allocating__ = true - Thread.critical = false - begin - @__instance__ = new - ensure - remove_instance_variable(:@__allocating__) - end - end - return @__instance__ - end - } - end + def Singleton.included (klass) + # should this be checked? + # raise TypeError.new "..." if klass.type == Module + 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 + @__instance__ = nil + def sub_klass.instance + unless @__instance__.nil? + # is the extra flexiblity having the hook method + # _wait() around ever useful? + _wait() while false.equal?(@__instance__) + # 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) + end + private :new, :allocate + # hook methods are also marked private + private :_load,:_wait + end + klass.inherited klass + end + private + def _dump(depth) + return "" + end end if __FILE__ == $0 - class SomeSingletonClass + +#basic example +class SomeSingletonClass include Singleton - #.... - end +end +a = SomeSingletonClass.instance +b = SomeSingletonClass.instance # a and b are same object +p a == b # => true +begin + SomeSingletonClass.new +rescue NoMethodError => mes + puts mes +end + +# threaded example with exception +Thread.abort_on_exception = true +class Ups < SomeSingletonClass + @__threads__= [] + @__flip__ = nil + @@__index__ = nil + + def initialize + sleep(rand(0.1)/10.0) + Thread.current[:index] = @@__index__ + end + class << self + def allocate + unless @__flip__ + @__flip__ = true + raise "boom - allocation in thread ##{@@__index__} aborted" + end + super() + end + def instanciate_all + 1.upto(5) do |@@__index__| + sleep(rand(0.1)/10.0) + @__threads__.push Thread.new { + begin + instance + rescue RuntimeError => mes + puts mes + end + } + end + end + def join + @__threads__.each do |t| + t.join + puts "initialize called by thread ##{t[:index]}" if +t[:index] + end + end + end +end + + +puts "There is(are) #{ObjectSpace.each_object(Ups) {}} Ups instance(s)" + # => The is(are) 0 Ups instance(s) +Ups.instanciate_all +Ups.join # => initialize called by thread # i - where i = 2 ... 5 +p Marshal.load(Marshal.dump(Ups.instance)) == Ups.instance # => true +puts "There is(are) #{ObjectSpace.each_object(Ups) {}} Ups instance(s)" + # => The is(are) 1 Ups instance(s) + +# Customized marshalling +class A + include Singleton + attr_accessor :persist, :die + def _dump(depth) + # this strips the @die information from the instance + Marshal.dump(@persist,depth) + end +end +def A._load(str) + instance.persist = Marshal.load(str) + instance +end + +a = A.instance +a.persist = ["persist"] +a.die = "die" + +stored_state = Marshal.dump(a) +# change state +a.persist = nil +a.die = nil +b = Marshal.load(stored_state) +p a == b # => true +p a.persist # => ["persist"] +p a.die # => nil - a = SomeSingletonClass.instance - b = SomeSingletonClass.instance # a and b are same object - p [a,b] - a = SomeSingletonClass.new # error (`new' is private) end diff --git a/marshal.c b/marshal.c index 8448f990e2..93005e9425 100644 --- a/marshal.c +++ b/marshal.c @@ -194,6 +194,10 @@ w_float(d, arg) else if (isnan(d)) { strcpy(buf, "nan"); } + else if (d == 0.0) { + if (1.0/d < 0) strcpy(buf, "-0"); + else strcpy(buf, "0"); + } else { /* xxx: should not use system's sprintf(3) */ sprintf(buf, "%.16g", d); -- cgit v1.2.3