summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog22
-rw-r--r--array.c24
-rw-r--r--enum.c24
-rw-r--r--eval.c15
-rw-r--r--ext/pty/extconf.rb2
-rw-r--r--ext/pty/pty.c10
-rw-r--r--gc.c53
-rw-r--r--lib/singleton.rb230
-rw-r--r--marshal.c4
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 <matz@ruby-lang.org>
+
+ * marshal.c (w_float): must distinguish -0.0 from 0.0.
+
Mon Nov 26 20:57:24 2001 Akinori MUSHA <knu@iDaemons.org>
* ext/Setup*, ext/syslog/*: import the "syslog" module from the
rough ruby project.
+Mon Nov 26 16:14:42 2001 K.Kosako <kosako@sofnec.co.jp>
+
+ * 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 <usa@ruby-lang.org>
* win32/win32.c (mypopen): fixed that mypclose() didn't really close
@@ -11,11 +23,21 @@ Mon Nov 26 16:54:59 2001 Usaku Nakamura <usa@ruby-lang.org>
* win32/win32.c (CreateChild): set STARTF_USESTDHANDLES flag only
when some handles are passed.
+Mon Nov 26 16:31:28 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * enum.c (sort_by_i): slight performance boost.
+
Sun Nov 25 21:02:18 2001 Usaku Nakamura <usa@ruby-lang.org>
* parse.y (str_extend): change types of second and third arguments
from char to int.
+Thu Nov 22 20:15:28 2001 TAMURA Takashi <sheepman@tcn.zaq.ne.jp>
+
+ * 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 <matz@ruby-lang.org>
* 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
@@ -1087,6 +1087,26 @@ rb_ary_sort(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; j<len; j++) {
+ RARRAY(e)->ptr[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; i<RARRAY(ary)->len; 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);