File: pure_ruby.rb

package info (click to toggle)
ruby-ref 1.0.5%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 332 kB
  • ctags: 186
  • sloc: ruby: 1,107; java: 92; makefile: 2
file content (100 lines) | stat: -rw-r--r-- 3,717 bytes parent folder | download
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
95
96
97
98
99
100
module Ref
  # This is a pure ruby implementation of a weak reference. It is much more
  # efficient than the WeakRef implementation bundled in MRI 1.8 and 1.9
  # subclass Delegator which is very heavy to instantiate and utilizes a
  # because it does not fair amount of memory under Ruby 1.8.
  class WeakReference < Reference
    
    class ReferencePointer
      def initialize(object)
        @referenced_object_id = object.__id__
        add_backreference(object)
      end
      
      def cleanup
        obj = ObjectSpace._id2ref(@referenced_object_id) rescue nil
        remove_backreference(obj) if obj
      end
      
      def object
        obj = ObjectSpace._id2ref(@referenced_object_id)
        obj if verify_backreferences(obj)
      rescue RangeError
        nil
      end
      
      private
        # Verify that the object is the same one originally set for the weak reference.
        def verify_backreferences(obj) #:nodoc:
          return nil unless supports_backreference?(obj)
          backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
          backreferences && backreferences.include?(object_id)
        end
      
        # Add a backreference to the object.
        def add_backreference(obj) #:nodoc:
          return unless supports_backreference?(obj)
          backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
          unless backreferences
            backreferences = []
            obj.instance_variable_set(:@__weak_backreferences__, backreferences)
          end
          backreferences << object_id
        end
      
        # Remove backreferences from the object.
        def remove_backreference(obj) #:nodoc:
          return unless supports_backreference?(obj)
          backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
          if backreferences
            backreferences.dup.delete(object_id)
            obj.send(:remove_instance_variable, :@__weak_backreferences__) if backreferences.empty?
          end
        end
        
        def supports_backreference?(obj)
          obj.respond_to?(:instance_variable_get) && obj.respond_to?(:instance_variable_defined?)
        rescue NoMethodError
          false
        end
    end
    
    @@weak_references = {}
    @@lock = SafeMonitor.new

    # Finalizer that cleans up weak references when references are destroyed.
    @@reference_finalizer = lambda do |object_id|
      @@lock.synchronize do
        reference_pointer = @@weak_references.delete(object_id)
        reference_pointer.cleanup if reference_pointer
      end
    end

    # Create a new weak reference to an object. The existence of the weak reference
    # will not prevent the garbage collector from reclaiming the referenced object.
    def initialize(obj) #:nodoc:
      @referenced_object_id = obj.__id__
      @@lock.synchronize do
        @reference_pointer = ReferencePointer.new(obj)
        @@weak_references[self.object_id] = @reference_pointer
      end
      ObjectSpace.define_finalizer(self, @@reference_finalizer)
    end

    # Get the reference object. If the object has already been garbage collected,
    # then this method will return nil.
    def object #:nodoc:
      if @reference_pointer
        obj = @reference_pointer.object
        unless obj
          @@lock.synchronize do
            @@weak_references.delete(object_id)
            @reference_pointer.cleanup
            @reference_pointer = nil
          end
        end
        obj
      end
    end
  end
end