lock(an_obj) { ..... }
similar to the lock library in perl.
"That won't not that hard to build", I thought. I did the first version in about 5 minutes. Then thought that it would be nice to make it robust to object_id wrap-around and also to discard unnecessary records associate to an_obj when an_obj has been garbage collected.
require 'thread' require 'weakref' class Lock Record = Struct.new("Record", :weakref, :mutex) def initialize(auto_cleanup_per_record = 1000) @mutex = Mutex.new @obj_records = {} @create_count = 0 @auto_cleanup_per_record = auto_cleanup_per_record end def lock(what, &block) record = acquire_obj_record(what) record.mutex.synchronize { if block_given? block.call end } end private def cleanup_proc lambda {|id| @mutex.synchronize { to_delete = [] @obj_records.each_pair{|k,v| if not v.weakref.weakref_alive? to_delete << k end } to_delete.each{|id| @obj_records.delete(id) } #puts "Cleanup id: #{to_delete.join(",")}. Size: #{@obj_records.size}: #{@obj_records.keys.join(",")}" } } end def acquire_obj_record(what) @mutex.synchronize { if record = @obj_records[what.object_id] and record.weakref.weakref_alive? record else ObjectSpace.define_finalizer(what, cleanup_proc) @obj_records[what.object_id] = Record.new(WeakRef.new(what), Mutex.new) end } end public @instance = Lock.new def self.lock(what, &block) @instance.lock(what, &block) end def self.cleanup(verbose=false) @instance.cleanup(verbose) end end if $0 == __FILE__ module Test def self.test1(finalizer_block) puts "Test1" s="str"*1024*1024 ObjectSpace.define_finalizer(s, finalizer_block) puts s.object_id t1 = Thread.new { Lock.lock(s) { 4.times { puts "Thread1" sleep(0.25) } } } t2 = Thread.new { puts "Thread2 started" Lock.lock(s) { 4.times { puts "Thread2" sleep(0.25) } } } t1.join t2.join end def self.test2(finalizer_block) puts "Test2" s="noo"*1024*1024 puts s.object_id ObjectSpace.define_finalizer(s, finalizer_block) Lock.lock(s) end def self.test3(finalizer_block) puts "Test3" 10.times { s="noo"*1024*1024 puts s.object_id ObjectSpace.define_finalizer(s, finalizer_block) Lock.lock(s) } end end Test.test1(lambda {|id| puts "Finalized: #{id}"}) GC.start puts "GC invoked" Test.test2(lambda {|id| puts "Finalized: #{id}"}) GC.start puts "GC invoked" Test.test3(lambda {|id| puts "Finalized: #{id}"}) GC.start puts "GC invoked" end =begin /mnt/vg0.home/ysantoso/tmp/ruby-postgres/tests $ ruby /tmp/lock.rb Test1 -604990708 Thread1 Thread2 started Thread1 Thread1 Thread1 Thread2 Thread2 Thread2 Thread2 GC invoked Test2 -610787788 GC invoked Test3 -610788148 -610953348 -615730956 -615809476 -615887996 Finalized: -615730956 Finalized: -610953348 -615902566 -615981086 -615697446 Finalized: -615809476 Finalized: -615887996 Finalized: -615902566 -614121002 -614199522 Finalized: -614121002 Cleanup id: -615809476,-615730956,-610953348,-615902566,-615887996. Size: 7: -614199522,-615697446,-615981086,-610787788,-614121002,-610788148,-604990708 Finalized: -614199522 Cleanup id: -614121002. Size: 6: -614199522,-615697446,-615981086,-610787788,-610788148,-604990708 Finalized: -615697446 Cleanup id: -614199522. Size: 5: -615697446,-615981086,-610787788,-610788148,-604990708 Finalized: -615981086 Cleanup id: -615697446. Size: 4: -615981086,-610787788,-610788148,-604990708 Finalized: -610787788 Cleanup id: -615981086. Size: 3: -610787788,-610788148,-604990708 Finalized: -610788148 Cleanup id: -610787788. Size: 2: -610788148,-604990708 GC invoked Finalized: -604990708 =end
(originally from http://microjet.ath.cx/WebWiki/2006.08.07_LockLibraryForRuby.html)
No comments:
Post a Comment