[前][次][番号順一覧][スレッド一覧]

ruby-changes:22946

From: nobu <ko1@a...>
Date: Tue, 13 Mar 2012 12:37:19 +0900 (JST)
Subject: [ruby-changes:22946] nobu:r34995 (trunk): Bug #5350

nobu	2012-03-13 12:37:06 +0900 (Tue, 13 Mar 2012)

  New Revision: 34995

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=34995

  Log:
    Bug #5350
    * gc.c: add ObjectSpace::WeakMap.  [ruby-dev:44565][Bug #5350]
    * lib/weakref.rb: use WeakMap instead of _id2ref.

  Added files:
    trunk/test/test_weakref.rb
  Modified files:
    trunk/ChangeLog
    trunk/gc.c
    trunk/lib/weakref.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 34994)
+++ ChangeLog	(revision 34995)
@@ -1,3 +1,11 @@
+Tue Mar 13 12:37:03 2012  Nobuyoshi Nakada  <nobu@r...>
+
+	Bug #5350
+
+	* gc.c: add ObjectSpace::WeakMap.  [ruby-dev:44565][Bug #5350]
+
+	* lib/weakref.rb: use WeakMap instead of _id2ref.
+
 Tue Mar 13 10:59:48 2012  Nobuyoshi Nakada  <nobu@r...>
 
 	* tool/rbinstall.rb (prepare): skip if basedir is not defined.
Index: lib/weakref.rb
===================================================================
--- lib/weakref.rb	(revision 34994)
+++ lib/weakref.rb	(revision 34995)
@@ -1,5 +1,4 @@
 require "delegate"
-require 'thread'
 
 # Weak Reference class that allows a referenced object to be
 # garbage-collected.  A WeakRef may be used exactly like the object it
@@ -24,51 +23,24 @@
   class RefError < StandardError
   end
 
-  @@id_map =  {}                # obj -> [ref,...]
-  @@id_rev_map =  {}            # ref -> obj
-  @@mutex = Mutex.new
-  @@final = lambda {|id|
-    @@mutex.synchronize {
-      rids = @@id_map[id]
-      if rids
-        for rid in rids
-          @@id_rev_map.delete(rid)
-        end
-        @@id_map.delete(id)
-      end
-      rid = @@id_rev_map[id]
-      if rid
-        @@id_rev_map.delete(id)
-        @@id_map[rid].delete(id)
-        @@id_map.delete(rid) if @@id_map[rid].empty?
-      end
-    }
-  }
+  @@__map = ::ObjectSpace::WeakMap.new
 
   ##
   # Creates a weak reference to +orig+
 
   def initialize(orig)
-    @__id = orig.object_id
-    ObjectSpace.define_finalizer orig, @@final
-    ObjectSpace.define_finalizer self, @@final
-    @@mutex.synchronize {
-      @@id_map[@__id] = [] unless @@id_map[@__id]
-    }
-    @@id_map[@__id].push self.object_id
-    @@id_rev_map[self.object_id] = @__id
+    case orig
+    when true, false, nil
+      @delegate_sd_obj = orig
+    else
+      @@__map[self] = orig
+    end
     super
   end
 
   def __getobj__ # :nodoc:
-    unless @@id_rev_map[self.object_id] == @__id
-      Kernel::raise RefError, "Invalid Reference - probably recycled", Kernel::caller(2)
-    end
-    begin
-      ObjectSpace._id2ref(@__id)
-    rescue RangeError
-      Kernel::raise RefError, "Invalid Reference - probably recycled", Kernel::caller(2)
-    end
+    @@__map[self] or defined?(@delegate_sd_obj) ? @delegate_sd_obj :
+      Kernel::raise(RefError, "Invalid Reference - probably recycled", Kernel::caller(2))
   end
 
   def __setobj__(obj) # :nodoc:
@@ -78,7 +50,7 @@
   # Returns true if the referenced object is still alive.
 
   def weakref_alive?
-    @@id_rev_map[self.object_id] == @__id
+    !!(@@__map[self] or defined?(@delegate_sd_obj))
   end
 end
 
Index: gc.c
===================================================================
--- gc.c	(revision 34994)
+++ gc.c	(revision 34995)
@@ -435,6 +435,9 @@
 #define HEAP_HEADER(p) ((struct heaps_header *)(p))
 
 static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
+static VALUE define_final0(VALUE obj, VALUE block);
+VALUE rb_define_final(VALUE obj, VALUE block);
+VALUE rb_undefine_final(VALUE obj);
 
 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
 rb_objspace_t *
@@ -1764,19 +1767,25 @@
     }
 }
 
+static int
+gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr)
+{
+    register uintptr_t *bits = GET_HEAP_BITMAP(ptr);
+    if (MARKED_IN_BITMAP(bits, ptr)) return 0;
+    MARK_IN_BITMAP(bits, ptr);
+    objspace->heap.live_num++;
+    return 1;
+}
+
 static void
 gc_mark(rb_objspace_t *objspace, VALUE ptr, int lev)
 {
     register RVALUE *obj;
-    register uintptr_t *bits;
 
     obj = RANY(ptr);
     if (rb_special_const_p(ptr)) return; /* special const not marked */
     if (obj->as.basic.flags == 0) return;       /* free cell */
-    bits = GET_HEAP_BITMAP(ptr);
-    if (MARKED_IN_BITMAP(bits, ptr)) return;  /* already marked */
-    MARK_IN_BITMAP(bits, ptr);
-    objspace->heap.live_num++;
+    if (!gc_mark_ptr(objspace, ptr)) return;	/* already marked */
 
     if (lev > GC_LEVEL_MAX || (lev == 0 && stack_check(STACKFRAME_FOR_GC_MARK))) {
 	if (!mark_stack_overflow) {
@@ -2998,6 +3007,12 @@
 static VALUE
 undefine_final(VALUE os, VALUE obj)
 {
+    return rb_undefine_final(obj);
+}
+
+VALUE
+rb_undefine_final(VALUE obj)
+{
     rb_objspace_t *objspace = &rb_objspace;
     st_data_t data = obj;
     rb_check_frozen(obj);
@@ -3018,9 +3033,7 @@
 static VALUE
 define_final(int argc, VALUE *argv, VALUE os)
 {
-    rb_objspace_t *objspace = &rb_objspace;
-    VALUE obj, block, table;
-    st_data_t data;
+    VALUE obj, block;
 
     rb_scan_args(argc, argv, "11", &obj, &block);
     rb_check_frozen(obj);
@@ -3031,6 +3044,16 @@
 	rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
 		 rb_obj_classname(block));
     }
+    return define_final0(obj, block);
+}
+
+static VALUE
+define_final0(VALUE obj, VALUE block)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    VALUE table;
+    st_data_t data;
+
     if (!FL_ABLE(obj)) {
 	rb_raise(rb_eArgError, "cannot define finalizer for %s",
 		 rb_obj_classname(obj));
@@ -3052,6 +3075,17 @@
     return block;
 }
 
+VALUE
+rb_define_final(VALUE obj, VALUE block)
+{
+    rb_check_frozen(obj);
+    if (!rb_respond_to(block, rb_intern("call"))) {
+	rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
+		 rb_obj_classname(block));
+    }
+    return define_final0(obj, block);
+}
+
 void
 rb_gc_copy_finalizer(VALUE dest, VALUE obj)
 {
@@ -3537,6 +3571,165 @@
 }
 
 /*
+ *  Document-class: ObjectSpace::WeakMap
+ *
+ *  An <code>ObjectSpace::WeakMap</code> object holds references to
+ *  any objects, but those objects can get disposed by GC.
+ */
+
+struct weakmap {
+    st_table *obj2wmap;		/* obj -> [ref,...] */
+    st_table *wmap2obj;		/* ref -> obj */
+    VALUE final;
+};
+
+static int
+wmap_mark_map(st_data_t key, st_data_t val, st_data_t arg)
+{
+    gc_mark_ptr((rb_objspace_t *)arg, (VALUE)val);
+    return ST_CONTINUE;
+}
+
+static void
+wmap_mark(void *ptr)
+{
+    struct weakmap *w = ptr;
+    st_foreach(w->obj2wmap, wmap_mark_map, (st_data_t)&rb_objspace);
+    rb_gc_mark(w->final);
+}
+
+static int
+wmap_free_map(st_data_t key, st_data_t val, st_data_t arg)
+{
+    rb_ary_resize((VALUE)val, 0);
+    return ST_CONTINUE;
+}
+
+static void
+wmap_free(void *ptr)
+{
+    struct weakmap *w = ptr;
+    st_foreach(w->obj2wmap, wmap_free_map, 0);
+    st_free_table(w->obj2wmap);
+    st_free_table(w->wmap2obj);
+}
+
+size_t rb_ary_memsize(VALUE ary);
+static int
+wmap_memsize_map(st_data_t key, st_data_t val, st_data_t arg)
+{
+    *(size_t *)arg += rb_ary_memsize((VALUE)val);
+    return ST_CONTINUE;
+}
+
+static size_t
+wmap_memsize(const void *ptr)
+{
+    size_t size;
+    const struct weakmap *w = ptr;
+    if (!w) return 0;
+    size = sizeof(*w);
+    size += st_memsize(w->obj2wmap);
+    size += st_memsize(w->wmap2obj);
+    st_foreach(w->obj2wmap, wmap_memsize_map, (st_data_t)&size);
+    return size;
+}
+
+static const rb_data_type_t weakmap_type = {
+    "weakmap",
+    {
+	wmap_mark,
+	wmap_free,
+	wmap_memsize,
+    }
+};
+
+static VALUE
+wmap_allocate(VALUE klass)
+{
+    struct weakmap *w;
+    VALUE obj = TypedData_Make_Struct(klass, struct weakmap, &weakmap_type, w);
+    w->obj2wmap = st_init_numtable();
+    w->wmap2obj = st_init_numtable();
+    w->final = rb_obj_method(obj, ID2SYM(rb_intern("finalize")));
+    return obj;
+}
+
+static int
+wmap_final_func(st_data_t key, st_data_t *value, st_data_t arg)
+{
+    VALUE obj = (VALUE)key, ary = (VALUE)*value;
+    rb_ary_delete(ary, obj);
+    if (!RARRAY_LEN(ary)) return ST_DELETE;
+    return ST_CONTINUE;
+}
+
+static VALUE
+wmap_finalize(VALUE self, VALUE obj)
+{
+    st_data_t data;
+    VALUE rids;
+    long i;
+    struct weakmap *w;
+
+    TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+    obj = NUM2PTR(obj);
+
+    data = (st_data_t)obj;
+    if (st_delete(w->obj2wmap, &data, &data)) {
+	rids = (VALUE)data;
+	for (i = 0; i < RARRAY_LEN(rids); ++i) {
+	    data = (st_data_t)RARRAY_PTR(rids)[i];
+	    st_delete(w->wmap2obj, &data, NULL);
+	}
+    }
+
+    data = (st_data_t)obj;
+    if (st_delete(w->wmap2obj, &data, &data)) {
+	st_update(w->obj2wmap, (st_data_t)obj, wmap_final_func, 0);
+    }
+    return self;
+}
+
+static VALUE
+wmap_aset(VALUE self, VALUE wmap, VALUE orig)
+{
+    st_data_t data;
+    VALUE rids;
+    struct weakmap *w;
+
+    TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+    rb_define_final(orig, w->final);
+    rb_define_final(wmap, w->final);
+    if (st_lookup(w->obj2wmap, (st_data_t)orig, &data)) {
+	rids = (VALUE)data;
+    }
+    else {
+	rids = rb_ary_tmp_new(1);
+	st_insert(w->obj2wmap, (st_data_t)orig, (st_data_t)rids);
+    }
+    rb_ary_push(rids, orig);
+    st_insert(w->wmap2obj, (st_data_t)wmap, (st_data_t)orig);
+    return nonspecial_obj_id(orig);
+}
+
+static VALUE
+wmap_aref(VALUE self, VALUE wmap)
+{
+    st_data_t data;
+    VALUE obj;
+    struct weakmap *w;
+    rb_objspace_t *objspace = &rb_objspace;
+
+    TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+    if (!st_lookup(w->wmap2obj, (st_data_t)wmap, &data)) return Qnil;
+    obj = (VALUE)data;
+    if (!is_id_value(objspace, obj)) return Qnil;
+    if (!is_live_object(objspace, obj)) return Qnil;
+    return obj;
+}
+
+/*
  *  call-seq:
  *     GC.count -> Integer
  *
@@ -3884,6 +4077,14 @@
 
     rb_define_module_function(rb_mObSpace, "count_objects", count_objects, -1);
 
+    {
+	VALUE rb_cWeakMap = rb_define_class_under(rb_mObSpace, "WeakMap", rb_cObject);
+	rb_define_alloc_func(rb_cWeakMap, wmap_allocate);
+	rb_define_method(rb_cWeakMap, "[]=", wmap_aset, 2);
+	rb_define_method(rb_cWeakMap, "[]", wmap_aref, 1);
+	rb_define_private_method(rb_cWeakMap, "finalize", wmap_finalize, 1);
+    }
+
 #if CALC_EXACT_MALLOC_SIZE
     rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);
     rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
Index: test/test_weakref.rb
===================================================================
--- test/test_weakref.rb	(revision 0)
+++ test/test_weakref.rb	(revision 34995)
@@ -0,0 +1,22 @@
+require 'test/unit'
+require 'weakref'
+
+class TestWeakRef < Test::Unit::TestCase
+  def make_weakref
+    obj = Object.new
+    return WeakRef.new(obj), obj.to_s
+  end
+
+  def test_ref
+    weak, str = make_weakref
+    assert_equal(str, weak.to_s)
+  end
+
+  def test_recycled
+    weak, str = make_weakref
+    assert_nothing_raised(WeakRef::RefError) {weak.to_s}
+    ObjectSpace.garbage_collect
+    ObjectSpace.garbage_collect
+    assert_raise(WeakRef::RefError) {weak.to_s}
+  end
+end

Property changes on: test/test_weakref.rb
___________________________________________________________________
Added: svn:eol-style
   + LF


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]