blob: ee5444a58477d93a360c144f88e70dcd37d3cff7 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
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
# references.
#
# 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)
class WeakRef < Delegator
##
# RefError is raised when a referenced object has been recycled by the
# garbage collector
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
}
}
##
# 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
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
end
def __setobj__(obj) # :nodoc:
end
##
# Returns true if the referenced object is still alive.
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
|