Skip to content

Commit 20c59ba

Browse files
committed
CI fix
1 parent 5f04dde commit 20c59ba

File tree

2 files changed

+41
-22
lines changed

2 files changed

+41
-22
lines changed

temporalio/test/gc_utils.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,23 +87,25 @@ def find_retaining_path_to(target_id, max_depth: 12, max_visits: 250_000, catego
8787
[objs, root_of[ids.first]]
8888
end
8989

90-
# Print path on stdout.
91-
def print_annotated_path(path, root_category:)
92-
puts "Retaining path (len=#{path.length}) from ROOT[:#{root_category}] to target:"
93-
return if path.empty?
90+
# Return string of annotated path
91+
def annotated_path(path, root_category:)
92+
lines = []
93+
lines << "Retaining path (len=#{path.length}) from ROOT[:#{root_category}] to target:"
94+
return lines.join("\n") if path.empty?
9495

9596
# First is the root
96-
puts " ROOT[:#{root_category}] #{describe_obj(path.first)}"
97+
lines << " ROOT[:#{root_category}] #{describe_obj(path.first)}"
9798
# Then edges with labels
9899
(0...(path.length - 1)).each do |i|
99100
parent = path[i]
100101
child = path[i + 1]
101102
labels = edge_labels(parent, child)
102103
labels.each_with_index do |lab, j|
103104
arrow = (j.zero? ? ' └─' : ' •')
104-
puts "#{arrow} via #{lab}#{describe_obj(child)}"
105+
lines << "#{arrow} via #{lab}#{describe_obj(child)}"
105106
end
106107
end
108+
lines.join("\n")
107109
end
108110

109111
private

temporalio/test/worker_workflow_test.rb

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,9 +1914,11 @@ class ConfirmGarbageCollectWorkflow < Temporalio::Workflow::Definition
19141914
@initialized_count = 0
19151915
@finalized_count = 0
19161916
@weak_instance = nil
1917+
@strong_instance = nil
1918+
@instance_object_id = nil
19171919

19181920
class << self
1919-
attr_accessor :initialized_count, :finalized_count, :weak_instance
1921+
attr_accessor :initialized_count, :finalized_count, :weak_instance, :strong_instance, :instance_object_id
19201922

19211923
def create_finalizer
19221924
proc { @finalized_count += 1 }
@@ -1926,6 +1928,10 @@ def create_finalizer
19261928
def initialize
19271929
self.class.initialized_count += 1
19281930
self.class.weak_instance = WeakRef.new(self)
1931+
# Uncomment this to cause test to fail
1932+
# self.class.strong_instance = self
1933+
self.class.instance_object_id = object_id
1934+
19291935
ObjectSpace.define_finalizer(self, self.class.create_finalizer)
19301936
end
19311937

@@ -1936,11 +1942,10 @@ def execute
19361942

19371943
def test_confirm_garbage_collect
19381944
major, minor = RUBY_VERSION.split('.').take(2).map(&:to_i)
1939-
skip('Only Ruby 3.4+ has predictable eager GC') if major != 3 || minor < 4
1945+
skip('Only Ruby 3.4+ has somewhat predictable eager GC') if major != 3 || minor < 4
19401946

19411947
# This test confirms the workflow instance is reliably GC'd when workflow/worker done. To confirm the test fails
1942-
# when there is still an instance, make a "strong_instance" singleton attribute and assign "self" to it in
1943-
# initialize and confirm this calls flunk later.
1948+
# when there is still an instance, uncomment the strong_instance set in the initialize of the workflow.
19441949

19451950
execute_workflow(ConfirmGarbageCollectWorkflow) do |handle|
19461951
# Wait until it is started
@@ -1950,18 +1955,30 @@ def test_confirm_garbage_collect
19501955
assert_equal 0, ConfirmGarbageCollectWorkflow.finalized_count
19511956
end
19521957

1953-
# Perform a GC and confirm gone
1954-
GC.start
1955-
begin
1956-
# Access instance and assert that it fails when a method is called on it as expected
1957-
instance = ConfirmGarbageCollectWorkflow.weak_instance.__getobj__
1958-
1959-
# Print out the path still holding it
1960-
path, cat = GCUtils.find_retaining_path_to(instance.object_id, max_depth: 12)
1961-
GCUtils.print_annotated_path(path, root_category: cat)
1962-
flunk
1963-
rescue WeakRef::RefError
1964-
# Expected
1958+
# Perform a GC and confirm gone. There are cases in Ruby where dead stack slots leave the item around for a bit, so
1959+
# we check repeatedly for a bit (every 200ms for 10s). We can't use assert_eventually, because path doesn't show
1960+
# well.
1961+
start_time = Time.now
1962+
loop do
1963+
GC.start
1964+
# Break if the instance is gone
1965+
break unless ConfirmGarbageCollectWorkflow.weak_instance.weakref_alive?
1966+
1967+
# If this is last iteration, flunk w/ the path
1968+
if Time.now - start_time > 10
1969+
path, cat = GCUtils.find_retaining_path_to(ConfirmGarbageCollectWorkflow.instance_object_id, max_depth: 12)
1970+
msg = GCUtils.annotated_path(path, root_category: cat)
1971+
msg += "\nPath:\n#{path.map { |p| " Item: #{p}" }.join("\n")}"
1972+
# Also display any Thread/Fiber backtraces that are in the path
1973+
path.grep(Thread).each do |thread|
1974+
msg += "\nThread trace: #{thread.backtrace.join("\n")}"
1975+
end
1976+
path.grep(Fiber).each do |fiber|
1977+
msg += "\nFiber trace: #{fiber.backtrace.join("\n")}"
1978+
end
1979+
flunk msg
1980+
end
1981+
sleep(0.2)
19651982
end
19661983
end
19671984

0 commit comments

Comments
 (0)