summaryrefslogtreecommitdiff
path: root/trunk/lib/weakref.rb
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/lib/weakref.rb')
-rw-r--r--trunk/lib/weakref.rb80
1 files changed, 80 insertions, 0 deletions
diff --git a/trunk/lib/weakref.rb b/trunk/lib/weakref.rb
new file mode 100644
index 0000000000..ba39242445
--- /dev/null
+++ b/trunk/lib/weakref.rb
@@ -0,0 +1,80 @@
+# Weak Reference class that does not bother GCing.
+#
+# Usage:
+# foo = Object.new
+# foo = Object.new
+# p foo.to_s # original's class
+# foo = WeakRef.new(foo)
+# p foo.to_s # should be same class
+# ObjectSpace.garbage_collect
+# p foo.to_s # should raise exception (recycled)
+
+require "delegate"
+require 'thread'
+
+class WeakRef < Delegator
+
+ 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
+ }
+ }
+
+ 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
+ super
+ end
+
+ def __getobj__
+ 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
+ end
+ def __setobj__(obj)
+ end
+
+ def weakref_alive?
+ @@id_rev_map[self.object_id] == @__id
+ end
+end
+
+if __FILE__ == $0
+# require 'thread'
+ foo = Object.new
+ p foo.to_s # original's class
+ foo = WeakRef.new(foo)
+ p foo.to_s # should be same class
+ ObjectSpace.garbage_collect
+ ObjectSpace.garbage_collect
+ p foo.to_s # should raise exception (recycled)
+end