@@ -1914,9 +1914,13 @@ 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 ,
1922+ :weak_instance , :strong_instance , :instance_object_id ,
1923+ :weak_fiber , :fiber_object_id
19201924
19211925 def create_finalizer
19221926 proc { @finalized_count += 1 }
@@ -1926,6 +1930,12 @@ def create_finalizer
19261930 def initialize
19271931 self . class . initialized_count += 1
19281932 self . class . weak_instance = WeakRef . new ( self )
1933+ # Uncomment this to cause test to fail
1934+ # self.class.strong_instance = self
1935+ self . class . instance_object_id = object_id
1936+ self . class . weak_fiber = WeakRef . new ( Fiber . current )
1937+ self . class . fiber_object_id = Fiber . current . object_id
1938+
19291939 ObjectSpace . define_finalizer ( self , self . class . create_finalizer )
19301940 end
19311941
@@ -1935,12 +1945,10 @@ def execute
19351945 end
19361946
19371947 def test_confirm_garbage_collect
1938- 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
1948+ skip ( 'Skipping GC collection confirmation until https://github.com/temporalio/sdk-ruby/issues/334' )
19401949
19411950 # 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.
1951+ # when there is still an instance, uncomment the strong_instance set in the initialize of the workflow.
19441952
19451953 execute_workflow ( ConfirmGarbageCollectWorkflow ) do |handle |
19461954 # Wait until it is started
@@ -1950,18 +1958,31 @@ def test_confirm_garbage_collect
19501958 assert_equal 0 , ConfirmGarbageCollectWorkflow . finalized_count
19511959 end
19521960
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
1961+ # Perform a GC and confirm gone. There are cases in Ruby where dead stack slots leave the item around for a bit, so
1962+ # we check repeatedly for a bit (every 200ms for 10s). We can't use assert_eventually, because path doesn't show
1963+ # well.
1964+ start_time = Time . now
1965+ loop do
1966+ GC . start
1967+ # Break if the instance is gone
1968+ break unless ConfirmGarbageCollectWorkflow . weak_fiber . weakref_alive?
1969+
1970+ # If this is last iteration, flunk w/ the path
1971+ if Time . now - start_time > 10
1972+ path , cat = GCUtils . find_retaining_path_to ( ConfirmGarbageCollectWorkflow . fiber_object_id , max_depth : 12 )
1973+ msg = GCUtils . annotated_path ( path , root_category : cat )
1974+ msg += "\n Path:\n #{ path . map { |p | " Item: #{ p } " } . join ( "\n " ) } "
1975+ # Also display any Thread/Fiber backtraces that are in the path
1976+ path . grep ( Thread ) . each do |thread |
1977+ msg += "\n Thread trace: #{ thread . backtrace . join ( "\n " ) } "
1978+ end
1979+ path . grep ( Fiber ) . each do |fiber |
1980+ msg += "\n Fiber trace: #{ fiber . backtrace . join ( "\n " ) } "
1981+ end
1982+ msg += "\n Orig fiber trace: #{ ConfirmGarbageCollectWorkflow . weak_fiber . backtrace . join ( "\n " ) } "
1983+ flunk msg
1984+ end
1985+ sleep ( 0.2 )
19651986 end
19661987 end
19671988
0 commit comments