sequence 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/weakrefset.rb ADDED
@@ -0,0 +1,254 @@
1
+ # $Id$
2
+ # Copyright (C) 2006 Caleb Clausen
3
+ # Distributed under the terms of Ruby's license.
4
+
5
+ require 'yaml'
6
+ require 'assert'
7
+
8
+ # WeakRefSet implements an unordered collection of weak references to objects.
9
+ # These references don't prevent garbage collection on these objects. As these
10
+ # objects are thrown away so does their entry in a WeakRefSet. Immmediate
11
+ # objects are not handled by this class (and wouldn't be useful).
12
+ class WeakRefSet
13
+ include Enumerable
14
+ # create a new WeakRefSet from an optional Enumerable (of objects)
15
+ # which is optionally processed through a block
16
+ def initialize(items) # :yield: obj
17
+ replace(items)
18
+ end
19
+ class<<self
20
+ def [] *items
21
+ new(items)
22
+ end
23
+ end
24
+
25
+ private
26
+ def finalizer(id)
27
+ @ids.delete(id)
28
+ end
29
+
30
+ public
31
+
32
+
33
+
34
+ # add a weak reference to the set
35
+ def add(obj)
36
+ Symbol===obj || Fixnum===obj || nil==obj || true==obj || false==obj and
37
+ raise ArgumentError, "no immediates in weakrefset"
38
+ id=obj.object_id
39
+ case (o2=ObjectSpace._id2ref id) #test id for validity
40
+ when Symbol,Fixnum,true,false,nil: id=obj #hopefully rare
41
+ else obj.equal? o2 or raise
42
+ ObjectSpace.define_finalizer(obj,method(:finalizer))
43
+ end
44
+ @ids[id] = true
45
+ self
46
+ end
47
+ alias << add
48
+ # iterate over remaining valid objects in the set
49
+ def each
50
+ @ids.each_key { |id|
51
+ case id
52
+ when Integer:
53
+ begin
54
+ o = ObjectSpace._id2ref(id)
55
+ rescue RangeError
56
+ next
57
+ end
58
+ @ids.include?(id) or next
59
+ #i don't know where the random symbols come from, but at least they're always symbols...
60
+ else
61
+ o=id
62
+ end
63
+ case o
64
+ when Symbol,Fixnum,true,false,nil: warn "immediate value #{o.inspect} found in weakrefset"
65
+ else yield(o)
66
+ end
67
+ }
68
+ self
69
+ end
70
+
71
+ def == other
72
+ size==other.size and
73
+ each{|x|
74
+ other.include? x or return
75
+ }
76
+
77
+ end
78
+
79
+ # clear the set (return self)
80
+ def clear
81
+ @ids = {}
82
+ self
83
+ end
84
+ # merge some more objects into the set (return self)
85
+ def merge(enum)
86
+ enum.each { |obj| add(obj) }
87
+ self
88
+ end
89
+ # replace the objects in the set (return self)
90
+ def replace(enum)
91
+ clear
92
+ merge(enum)
93
+ self
94
+ end
95
+ # delete an object in the set (return self)
96
+ def delete(obj)
97
+ delete?(obj)
98
+ self
99
+ end
100
+ # delete an object in the set (return self or nil if nothing deleted)
101
+ def delete?(obj)
102
+ x=include?(obj) and @ids.delete(x.__id__)||@ids.delete(x) and self
103
+ end
104
+ # is this object in the set?
105
+ def include?(obj)
106
+ find{|x| obj==x}
107
+ end
108
+ alias member? include?
109
+
110
+ # return a human-readable string showing the set
111
+ def inspect
112
+ #unless $weakrefset_verbose_inspect
113
+ # return sprintf('#<%s:0x%x {...}>', self.class.name, object_id)
114
+ #end
115
+ ids = (Thread.current[:__weakrefset__inspect_key__] ||= [])
116
+
117
+ if ids.include?(object_id)
118
+ return sprintf('#<%s {...}>', self.class.name)
119
+ end
120
+
121
+ begin
122
+ ids << object_id
123
+ return sprintf('#<%s {%s}>', self.class.name, to_a.inspect[1..-2])
124
+ ensure
125
+ ids.pop
126
+ Thread.current[:__weakrefset__inspect_key__].empty? and
127
+ Thread.current[:__weakrefset__inspect_key__]=nil
128
+ end
129
+ end
130
+
131
+ if false #this is broken; emits yaml for a hash.
132
+
133
+ YAML::add_domain_type( "inforadical.net,2005", "object:WeakRefSet" ) do |type, val|
134
+ WeakRefSet.new( *val["items"] )
135
+ end
136
+
137
+ def is_complex_yaml?; true end
138
+
139
+ def to_yaml_type; "!inforadical.net,2005/object:WeakRefSet" end
140
+
141
+ alias to_yaml_properties to_a
142
+
143
+ def to_yaml( opts = {} )
144
+ YAML::quick_emit( object_id, opts ) { |out|
145
+ out.map( to_yaml_type ) { |map|
146
+ map.add( "items", to_yaml_properties)
147
+ }
148
+ }
149
+ end
150
+ end
151
+
152
+ # remove some objects from the set (return self)
153
+ def subtract(enum)
154
+ enum.each { |obj| delete(obj) }
155
+ self
156
+ end
157
+ # any objects in the set still valid?
158
+ def empty?
159
+ @ids.empty?
160
+ end
161
+ # number of objects in the set still valid
162
+ def size
163
+ @ids.size
164
+ end
165
+ alias length size
166
+ end
167
+
168
+ # :stopdoc:
169
+
170
+ if __FILE__==$0
171
+ require 'benchmark'
172
+ class MyString <String;
173
+ def initialize(*)
174
+ @owner=0
175
+ super
176
+ end
177
+
178
+ end
179
+ class MyObject; end
180
+ class MyClass < Module; end
181
+ weakrefsets = (1..10).map {WeakRefSet[]}
182
+ $stdout.sync=true
183
+ obj = nil
184
+ arr=[]
185
+ # srand(2389547343)
186
+ classes=[]
187
+ ObjectSpace.each_object(Class){|ob| classes<<ob}
188
+ classes-=[Symbol,Integer,NilClass,FalseClass,TrueClass,Numeric,Data,Bignum,Fixnum,
189
+ Float,Struct,Method,UnboundMethod,Proc,Thread,Binding,Continuation]
190
+ classes.delete_if{|k| begin k.allocate; rescue; true else false end}
191
+ def shuffle!(arr)
192
+ arr.sort_by{rand}
193
+ end
194
+ times = Benchmark.measure {
195
+ 100000.times { |i|
196
+ # print(weakrefs.size>70?"|":((60..70)===weakrefs.size ? ":" : (weakrefs.size>50?',':'.')))
197
+ print "." if 0==i%128
198
+ #obj = (k=classes[rand(classes.size)]).allocate
199
+ obj = (k=MyString).new "X" #*rand(i+1)
200
+ # obj= (k=Object).new
201
+ # obj= (k=MyObject).new
202
+ #obj= (k=MyClass).new
203
+ k==obj.class or raise
204
+ weakrefs=weakrefsets[rand(weakrefsets.size)]
205
+ obj.instance_eval{@owner=weakrefs}
206
+ obj.instance_eval{@owner}.equal? weakrefs or raise
207
+ weakrefs.each { |o|
208
+ # k==o.class or raise "set contained a #{o.class}. i=#{i}. size=#{weakrefs.size}"
209
+ (o2=o.instance_eval{@owner})==weakrefs or
210
+ raise "expected owner #{weakrefs.map{|w| w.__id__}.inspect}, "+
211
+ "got #{o2.inspect}, item #{o}, id #{o.__id__}, obj #{obj.__id__}"
212
+ }
213
+ weakrefs << obj
214
+ weakrefs.each { |o|
215
+ # k==o.class or raise "set contained a #{o.class}. i=#{i}. size=#{weakrefs.size}"
216
+ (o2=o.instance_eval{@owner})==weakrefs or
217
+ raise "expected owner #{weakrefs.map{|w| w.__id__}.inspect}, "+
218
+ "got #{o2.inspect}, item #{o}, id #{o.__id__}, obj #{obj.__id__}"
219
+ }
220
+ weakrefs.include?(obj) or raise
221
+ weakrefs.each { |o|
222
+ # k==o.class or raise "set contained a #{o.class}. i=#{i}. size=#{weakrefs.size}"
223
+ (o2=o.instance_eval{@owner})==weakrefs or
224
+ raise "expected owner #{weakrefs.map{|w| w.__id__}.inspect}, "+
225
+ "got #{o2.inspect}, item #{o}, id #{o.__id__}, obj #{obj.__id__}"
226
+ }
227
+ weakrefs.include?(obj) or raise
228
+ if rand(10).zero?
229
+ weakrefs.delete?(obj) or raise
230
+ !weakrefs.include?(obj) or raise
231
+ elsif rand(4).zero?
232
+ arr<<obj #prevent garbage collection
233
+ end
234
+ if rand(1000).zero?
235
+ shuffle! arr
236
+ arr.slice!(0..rand(arr.size))
237
+ end
238
+ arr.each{|o| o.instance_eval{@owner}.include? o or raise }
239
+ #rand(100).zero? and GC.start
240
+ }
241
+ }
242
+ puts
243
+ GC.start
244
+ weakrefsets.each{|weakrefs|
245
+ weakrefs.clear
246
+ weakrefs.size.zero? or raise
247
+ weakrefs.empty? or raise
248
+ }
249
+ puts(times)
250
+ end
251
+
252
+ # :stopdoc:
253
+
254
+