From: "parker (Parker Finch)" Date: 2022-10-13T15:17:23+00:00 Subject: [ruby-core:110276] [Ruby master Bug#19041] Weakref is still alive after major garbage collection Issue #19041 has been updated by parker (Parker Finch). Thanks @byroot! I think this could be considered a bug in the documentation, since the [docs for WeakRef](https://ruby-doc.org/stdlib-3.1.2/libdoc/weakref/rdoc/WeakRef.html) imply that a `WeakRef` should be collected after a garbage collection. Perhaps we could call this corner-case out? I'm also curious to learn more about this case. (I'm unfamiliar with Ruby's use of registers and how that interacts with live objects and garbage collection.) It seems like calling the `weakref_alive?` method is continually forcing the object ref into a register, and sleeping after calling that method gives time for the register to clear. Is that understanding correct? (I'm surprised that calling a method on the `WeakRef` object prevents the underlying object from being collected, since shouldn't that underlying one be collected even though the `WeakRef` itself still has a reference? Does the method call put the underlying object ref in a register?) Is there a more reliable/direct way to get rid of the reference than sleeping? One aspect of this where I'm still confused is why the loop given to reproduce this issue completes an iteration before hanging. What is different on the first iteration that allows this to succeed? ---------------------------------------- Bug #19041: Weakref is still alive after major garbage collection https://bugs.ruby-lang.org/issues/19041#change-99563 * Author: parker (Parker Finch) * Status: Closed * Priority: Normal * ruby -v: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin21] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- I am able to get into an infinite loop waiting for garbage collection to take a WeakRef. ### Reproduction Process The following script prints a "0", then a "1", and then hangs forever. I expect it to keep printing numbers. ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) GC.start while obj.weakref_alive? iterations += 1 end ``` ### Ruby Version I have tested this on Ruby 3.1.2, 3.1.0, 3.0.4, 3.0.0, 2.7.6, and 2.7.0 on macOS. All exhibit this behavior. ### Further Investigation #### Sleeping Sleeping before the garbage collection allows the loop to continue. The below exhibits the expected behavior: ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) (sleep(0.5); GC.start) while obj.weakref_alive? iterations += 1 end ``` However, sleeping _after_ the garbage collection still shows the buggy behavior (loop hangs): ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) (GC.start; sleep(0.5)) while obj.weakref_alive? iterations += 1 end ``` #### Running Garbage Collection Multiple Times Explicitly running garbage collection multiple times allows the loop to continue. This has the expected behavior, more numbers continue to be printed: ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) while obj.weakref_alive? GC.start GC.start GC.start end iterations += 1 end ``` However, with certain rubies, running those garbage collection calls in a `times` block prevents even a single iteration from completing. The following prints only "0" with ruby 3.0.4 on macOS, ruby 2.7.6 on macOS, and ruby 3.1.2 on linux (`ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]` on a virtual machine). It shows the expected behavior on ruby 3.1.2 on macOS. ``` require "weakref" iterations = 0 loop do print "\r#{iterations}" obj = WeakRef.new(Object.new) 3.times { GC.start } while obj.weakref_alive? iterations += 1 end ``` -- https://bugs.ruby-lang.org/ Unsubscribe: