From: Eric Mahurin Date: 2005-06-21T08:35:00+09:00 Subject: WeakRefList (was Re: GC.disable not working?) 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