mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Use weak references in descendants tracker
It allows anonymous subclasses to be garbage collected.
This commit is contained in:
parent
15ca8ad0c1
commit
7432c9226e
3 changed files with 64 additions and 4 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
* Use weak references in descendants tracker to allow anonymous subclasses to
|
||||||
|
be garbage collected.
|
||||||
|
|
||||||
|
*Edgars Beigarts*
|
||||||
|
|
||||||
* Update `ActiveSupport::Notifications::Instrumenter#instrument` to make
|
* Update `ActiveSupport::Notifications::Instrumenter#instrument` to make
|
||||||
passing a block optional. This will let users use
|
passing a block optional. This will let users use
|
||||||
`ActiveSupport::Notifications` messaging features outside of
|
`ActiveSupport::Notifications` messaging features outside of
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "weakref"
|
||||||
|
|
||||||
module ActiveSupport
|
module ActiveSupport
|
||||||
# This module provides an internal implementation to track descendants
|
# This module provides an internal implementation to track descendants
|
||||||
# which is faster than iterating through ObjectSpace.
|
# which is faster than iterating through ObjectSpace.
|
||||||
|
@ -8,7 +10,8 @@ module ActiveSupport
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def direct_descendants(klass)
|
def direct_descendants(klass)
|
||||||
@@direct_descendants[klass] || []
|
descendants = @@direct_descendants[klass]
|
||||||
|
descendants ? descendants.to_a : []
|
||||||
end
|
end
|
||||||
|
|
||||||
def descendants(klass)
|
def descendants(klass)
|
||||||
|
@ -34,15 +37,17 @@ module ActiveSupport
|
||||||
# This is the only method that is not thread safe, but is only ever called
|
# This is the only method that is not thread safe, but is only ever called
|
||||||
# during the eager loading phase.
|
# during the eager loading phase.
|
||||||
def store_inherited(klass, descendant)
|
def store_inherited(klass, descendant)
|
||||||
(@@direct_descendants[klass] ||= []) << descendant
|
(@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def accumulate_descendants(klass, acc)
|
def accumulate_descendants(klass, acc)
|
||||||
if direct_descendants = @@direct_descendants[klass]
|
if direct_descendants = @@direct_descendants[klass]
|
||||||
acc.concat(direct_descendants)
|
direct_descendants.each do |direct_descendant|
|
||||||
direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
|
acc << direct_descendant
|
||||||
|
accumulate_descendants(direct_descendant, acc)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -59,5 +64,46 @@ module ActiveSupport
|
||||||
def descendants
|
def descendants
|
||||||
DescendantsTracker.descendants(self)
|
DescendantsTracker.descendants(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# DescendantsArray is an array that contains weak references to classes.
|
||||||
|
class DescendantsArray # :nodoc:
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@refs = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_copy(orig)
|
||||||
|
@refs = @refs.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
def <<(klass)
|
||||||
|
cleanup!
|
||||||
|
@refs << WeakRef.new(klass)
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
@refs.each do |ref|
|
||||||
|
yield ref.__getobj__
|
||||||
|
rescue WeakRef::RefError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def refs_size
|
||||||
|
@refs.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleanup!
|
||||||
|
@refs.delete_if { |ref| !ref.weakref_alive? }
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject!
|
||||||
|
@refs.reject! do |ref|
|
||||||
|
yield ref.__getobj__
|
||||||
|
rescue WeakRef::RefError
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,15 @@ module DescendantsTrackerTestCases
|
||||||
assert_equal_sets [], Child2.descendants
|
assert_equal_sets [], Child2.descendants
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_descendants_with_garbage_collected_classes
|
||||||
|
1.times do
|
||||||
|
child_klass = Class.new(Parent)
|
||||||
|
assert_equal_sets [Child1, Grandchild1, Grandchild2, Child2, child_klass], Parent.descendants
|
||||||
|
end
|
||||||
|
GC.start
|
||||||
|
assert_equal_sets [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants
|
||||||
|
end
|
||||||
|
|
||||||
def test_direct_descendants
|
def test_direct_descendants
|
||||||
assert_equal_sets [Child1, Child2], Parent.direct_descendants
|
assert_equal_sets [Child1, Child2], Parent.direct_descendants
|
||||||
assert_equal_sets [Grandchild1, Grandchild2], Child1.direct_descendants
|
assert_equal_sets [Grandchild1, Grandchild2], Child1.direct_descendants
|
||||||
|
|
Loading…
Reference in a new issue