$search
00001 #include <set> 00002 #include <map> 00003 #include <ruby.h> 00004 #ifdef RUBY_IS_19 00005 #include <ruby/intern.h> 00006 #else 00007 #include <intern.h> 00008 #endif 00009 00010 using std::set; 00011 using std::map; 00012 00013 static VALUE cWeakRef; 00014 static VALUE cRefError; 00015 00016 /* Weakref internal structure. +obj+ is Qnil before initialization and Qundef 00017 * after finalization */ 00018 struct WeakRef { 00019 VALUE ruby_ref; 00020 VALUE obj; 00021 }; 00022 00023 // Map from real objects to the set of associated WeakRef objects 00024 typedef set<VALUE> ObjSet; 00025 typedef map< VALUE, ObjSet > RefFromObjID; 00026 typedef map< VALUE, VALUE > ObjFromRefID; 00027 RefFromObjID from_obj_id; 00028 ObjFromRefID from_ref_id; 00029 00030 static void weakref_free(WeakRef const* ref) 00031 { 00032 VALUE ref_id = rb_obj_id(ref->ruby_ref); 00033 ObjFromRefID::iterator obj_it = from_ref_id.find(ref_id); 00034 if (obj_it != from_ref_id.end()) 00035 { 00036 VALUE obj_id = rb_obj_id(obj_it->second); 00037 RefFromObjID::iterator ref_set = from_obj_id.find(obj_id); 00038 ref_set->second.erase(ref->ruby_ref); 00039 from_ref_id.erase(obj_it); 00040 } 00041 delete ref; 00042 } 00043 static VALUE weakref_alloc(VALUE klass) 00044 { 00045 WeakRef* ref = new WeakRef; 00046 ref->obj = Qnil; 00047 ref->ruby_ref = Data_Wrap_Struct(klass, NULL, weakref_free, ref); 00048 return ref->ruby_ref; 00049 } 00050 00051 static WeakRef& get_weakref(VALUE self) 00052 { 00053 WeakRef* object = 0; 00054 Data_Get_Struct(self, WeakRef, object); 00055 return *object; 00056 } 00057 00058 static VALUE do_object_finalize(VALUE mod, VALUE obj_id) 00059 { 00060 RefFromObjID::iterator ref_set = from_obj_id.find(obj_id); 00061 if (ref_set != from_obj_id.end()) 00062 { 00063 ObjSet::iterator it = ref_set->second.begin(); 00064 ObjSet::iterator const end = ref_set->second.end(); 00065 for (; it != end; ++it) 00066 { 00067 // Do NOT use Data_Wrap_Struct here, it would break on recent Ruby 00068 // interpreters. The reason is that the object type is reset during 00069 // GC -- and the call to free functions is deferred. 00070 // 00071 // So, at this point, we're sure we have a RDATA in *it (otherwise 00072 // weakref_free would have been called), but we can't use 00073 // Data_Wrap_Struct because that would crash. 00074 WeakRef& ref = *reinterpret_cast<WeakRef*>(RDATA(*it)->data);; 00075 ref.obj = Qundef; 00076 from_ref_id.erase(rb_obj_id(*it)); 00077 } 00078 from_obj_id.erase(obj_id); 00079 } 00080 return Qnil; 00081 } 00082 00083 // Note: the Ruby code has already registered +do_object_finalize+ as the 00084 // finalizer for +obj+. 00085 // 00086 // It is forbidden to make a weakref-of-weakref or a weakref of an immediate 00087 // object 00088 static VALUE weakref_do_initialize(VALUE self, VALUE obj) 00089 { 00090 if (!FL_ABLE(obj)) 00091 { 00092 VALUE str = rb_any_to_s(obj); 00093 rb_raise(rb_eArgError, "%s cannot be finalized", StringValuePtr(str)); 00094 } 00095 00096 WeakRef& ref = get_weakref(self); 00097 ref.obj = obj; 00098 00099 RefFromObjID::iterator it = from_obj_id.find(rb_obj_id(obj)); 00100 if (it == from_obj_id.end()) 00101 it = from_obj_id.insert( make_pair(rb_obj_id(obj), ObjSet()) ).first; 00102 00103 it->second.insert(self); 00104 from_ref_id.insert( std::make_pair(rb_obj_id(self), obj) ); 00105 00106 return Qnil; 00107 } 00108 00109 static VALUE weakref_get(VALUE self) 00110 { 00111 WeakRef const& ref = get_weakref(self); 00112 00113 if (ref.obj == Qnil) 00114 rb_raise(cRefError, "initialized weakref"); 00115 if (ref.obj == Qundef) 00116 rb_raise(cRefError, "finalized object"); 00117 return ref.obj; 00118 } 00119 00120 static VALUE refcount(VALUE mod, VALUE obj) 00121 { 00122 if (0 == (obj & FIXNUM_FLAG)) 00123 obj = rb_obj_id(obj); 00124 00125 RefFromObjID::const_iterator it = from_obj_id.find(obj); 00126 if (it == from_obj_id.end()) 00127 return Qnil; 00128 else 00129 return INT2FIX(it->second.size()); 00130 } 00131 00132 extern "C" void Init_weakref(VALUE mUtilrb) 00133 { 00134 cWeakRef = rb_define_class_under(mUtilrb, "WeakRef", rb_cObject); 00135 cRefError = rb_define_class_under(cWeakRef, "RefError", rb_eStandardError); 00136 rb_define_alloc_func(cWeakRef, weakref_alloc); 00137 00138 rb_define_singleton_method(cWeakRef, "do_object_finalize", RUBY_METHOD_FUNC(do_object_finalize), 1); 00139 rb_define_singleton_method(cWeakRef, "refcount", RUBY_METHOD_FUNC(refcount), 1); 00140 rb_define_method(cWeakRef, "do_initialize", RUBY_METHOD_FUNC(weakref_do_initialize), 1); 00141 rb_define_method(cWeakRef, "get", RUBY_METHOD_FUNC(weakref_get), 0); 00142 } 00143