Class: Foobara::WeakObjectHash

Inherits:
Object
  • Object
show all
Defined in:
foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb,
foobara-0.2.2/projects/weak_object_hash/lib/foobara/weak_object_hash.rb

Overview

TODO: a possible optimization: have a certain number of records before the Weakref approach kicks in that way we don’t just immediately clear out useful information without any actual memory burden

Defined Under Namespace

Classes: ClosedError

Instance Method Summary collapse

Constructor Details

#initialize(skip_finalizer: false) ⇒ WeakObjectHash

Returns a new instance of WeakObjectHash.



9
10
11
12
13
14
15
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 9

def initialize(skip_finalizer: false)
  if skip_finalizer
    self.skip_finalizer = true
  end
  self.monitor = Monitor.new
  self.object_ids_to_weak_refs_and_values = {}
end

Instance Method Details

#[](object) ⇒ Object



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
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 38

def [](object)
  object_id = object.object_id

  if closed?
    raise ClosedError, "Cannot retrieve objects from a closed WeakObjectHash"
  end

  monitor.synchronize do
    pair = object_ids_to_weak_refs_and_values[object_id]

    return nil unless pair

    weak_ref, value = pair

    if weak_ref.weakref_alive?
      if skip_finalizer?
        if weak_ref.__getobj__ == object
          value
        else
          # :nocov:
          object_ids_to_weak_refs_and_values.delete(object_id)
          nil
          # :nocov:
        end
      else
        value
      end
    else
      # Seems unreachable... if it's been garbage collected how could we have a reference to the object
      # to pass it in?
      # :nocov:
      object_ids_to_weak_refs_and_values.delete(object_id)
      nil
      # :nocov:
    end
  end
end

#[]=(object, value) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 17

def []=(object, value)
  object_id = object.object_id
  weak_ref = WeakRef.new(object)

  if closed?
    raise ClosedError, "Cannot add objects to a closed WeakObjectHash"
  end

  monitor.synchronize do
    delete(object)

    object_ids_to_weak_refs_and_values[object_id] = [weak_ref, value]

    unless skip_finalizer?
      ObjectSpace.define_finalizer(object, finalizer_proc)
    end

    value
  end
end

#clearObject



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 189

def clear
  monitor.synchronize do
    unless skip_finalizer?
      object_ids_to_weak_refs_and_values.each_value do |pair|
        weak_ref = pair.first

        if weak_ref.weakref_alive?
          ObjectSpace.undefine_finalizer(weak_ref.__getobj__)
        end
      end
    end

    object_ids_to_weak_refs_and_values.clear
  end
end

#close!Object



175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 175

def close!
  if closed?
    raise ClosedError, "Already closed"
  end

  monitor.synchronize do
    self.closed = true
    clear
    @finalizer_proc = nil
    self.object_ids_to_weak_refs_and_values = nil
    self.monitor = nil
  end
end

#closed?Boolean

Returns:

  • (Boolean)


205
206
207
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 205

def closed?
  closed
end

#delete(object) ⇒ Object



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
101
102
103
104
105
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 76

def delete(object)
  object_id = object.object_id

  monitor.synchronize do
    pair = object_ids_to_weak_refs_and_values.delete(object_id)

    return nil unless pair

    weak_ref, value = pair

    if weak_ref.weakref_alive?

      if skip_finalizer?
        if weak_ref.__getobj__ == object
          value
        end
      else
        # Hmmm, there's seemingly no safe way to remove the finalizer for the previous entry
        # if it exists. This is because we can only remove all finalizers on object. Not only
        # the ones we've created.
        # We will just do this anyway with that caveat and maybe make this configuratble in the future.
        unless skip_finalizer?
          ObjectSpace.undefine_finalizer(object)
        end

        value
      end
    end
  end
end

#each_pairObject



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 107

def each_pair
  monitor.synchronize do
    object_ids_to_weak_refs_and_values.each_pair do |object_id, pair|
      weak_ref, value = pair

      if weak_ref.weakref_alive?
        yield weak_ref.__getobj__, value
      else
        object_ids_to_weak_refs_and_values.delete(object_id)
      end
    end
  end

  self
end

#empty?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 171

def empty?
  size == 0
end

#keysObject



135
136
137
138
139
140
141
142
143
144
145
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 135

def keys
  keys = []

  monitor.synchronize do
    each_pair do |key, _value|
      keys << key
    end
  end

  keys
end

#sizeObject



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 147

def size
  size = 0
  to_delete = nil

  monitor.synchronize do
    object_ids_to_weak_refs_and_values.each_pair do |object_id, pair|
      weak_ref = pair.first

      if weak_ref.weakref_alive?
        size += 1
      else
        to_delete ||= []
        to_delete << object_id
      end
    end

    to_delete&.each do |object_id|
      object_ids_to_weak_refs_and_values.delete(object_id)
    end
  end

  size
end

#skip_finalizer?Boolean

Returns:

  • (Boolean)


209
210
211
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 209

def skip_finalizer?
  @skip_finalizer
end

#valuesObject



123
124
125
126
127
128
129
130
131
132
133
# File 'foobara-0.2.2/projects/weak_object_hash/src/weak_object_hash.rb', line 123

def values
  values = []

  monitor.synchronize do
    each_pair do |_key, value|
      values << value
    end
  end

  values
end