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
00017
00018 struct WeakRef {
00019 VALUE ruby_ref;
00020 VALUE obj;
00021 };
00022
00023
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
00068
00069
00070
00071
00072
00073
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
00084
00085
00086
00087
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