I think I finally figured out a solution to making a set/list
of "WeakRef"s. The code is below. It mimics much of the Set
interface. This is an order of magnitude faster than (and less
memory) using WeakRef to implement this.
The main thing I was missing was to check to see if the object
was finalized after a successful _id2ref. If it was finalized
that means I got an object that reclaimed the space my old
object had.
I wasn't sure that you could delete elements of a hash while
iterating over it, but it looks to work fine. Should this be
OK? Anybody see any race conditions or other problems with
this?
#!/bin/env ruby
class WeakRefList
include Enumerable
def finalizer(id);@ids.delete(id >> 1);end
private :finalizer
def initialize(enum=[],&block)
replace(enum.collect(&block))
end
def add(o)
@ids[o.__id__ >> 1] = true
ObjectSpace.define_finalizer(o,method(:finalizer))
self
end
alias << add
def each(&block)
@ids.each_key { |id|
begin
o = ObjectSpace._id2ref(id << 1)
# double-check in case it was finalized
block.call(o) if @ids.include?(id)
rescue RangeError
end
}
nil
end
def clear;@ids = {};self;end
def merge(enum);enum.each{|o|add(o)};self;end
def replace(enum);clear;merge(enum);self;end
def delete(o);@ids.delete(o.__id__ >> 1);self;end
def delete?(o);@ids.delete(o.__id__ >> 1)&&self;end
def empty?;each{return(false)};true;end
def include?(o);@ids.include?(o.__id__ >> 1);end
alias member? include?
def inspect;"#<#{self.class}: #{to_a.inspect}>";end
def subtract(enum);enum.each{|o|delete(o)};self;end
def size;n=0;each{n+=1};n;end
alias length size
end
if __FILE__==$0
require 'benchmark'
class MyString < String; end
weakrefs = WeakRefList.new
$stdout.sync=true
times = Benchmark.measure {
10000.times { |i|
print(".")
obj = MyString.new("X"*rand(i+1))
weakrefs << obj
weakrefs.each { |o|
MyString==o.class or
raise("not a MyString: #{o.object_id}
#{o.inspect}")
}
weakrefs.include?(obj) or
raise("#{obj.inspect} disappeared")
if rand(10).zero?
weakrefs.delete(obj)
!weakrefs.include?(obj) or
raise("#{obj.inspect} didn't delete")
end
}
}
p weakrefs
p weakrefs.size
p weakrefs.empty?
p weakrefs.clear
p weakrefs.size
p weakrefs.empty?
puts(times)
end
__________________________________
Do you Yahoo!?
Make Yahoo! your home page
http://www.yahoo.com/r/hs