Project

General

Profile

Actions

Feature #18143

closed

Add a new method to change GC.stress only in the given block such as GC.with_stress(flag) {...}

Added by kou (Kouhei Sutou) about 3 years ago. Updated about 3 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
[ruby-core:105114]

Description

GC.stress = true is useful for detecting GC related crashes. We can use it for debugging GC related problems and testing the problem is solved.

Generally, we need to enable stress mode before the target code block and disable stress mode after the target code block:

GC.stress = true
# ... something buggy codes ...
GC.stress = false

Or we just enable stress mode before the target code block when the target code block causes a crash:

GC.stress = true
# ... something crash codes ...

In test code, we must disable stress mode because stress mode slows down test execution:

def test_gc
  GC.stress = true
  # ... GC related code ...
ensure
  GC.stress = false
end

We have an utility method in CRuby's test utility: EnvUtil.#under_gc_stress:

https://github.com/ruby/ruby/blob/ab63f6d8543903f177c46634f38e5428655f003b/tool/lib/envutil.rb#L236-L242

  def under_gc_stress(stress = true)
    stress, GC.stress = GC.stress, stress
    yield
  ensure
    GC.stress = stress
  end
  module_function :under_gc_stress

This feature is useful not only CRuby's test but also other libraries test and debugging a program that has a GC related problem.

How about adding a new singleton method that changes stress mode only in the given block? If we have the method, we don't need to implement a small utility method multiple times for the feature.

API candidates:

GC.with_stress(flag) {...}:

module GC
  def self.with_stress(flag)
    flag_old = stress
    self.stress = flag
    yield
  ensure
    self.stress = flag_old
  end
end

GC.under_stress {...}:

module GC
  def self.under_stress
    flag_old = stress
    self.stress = true
    yield
  ensure
    self.stress = flag_old
  end
end

GC.stress(flag = true) {...}:

module GC
  def stress flag = true
    if block_given?
      flag_old = Primitive.gc_stress_get
      begin
        GC.stress = flag
      ensure
        GC.stress = flag_old
      end
    else
      Primitive.gc_stress_get
    end
  end
end

Note:

  • Disadvantage is, GC.stress is a getter method and GC.stress do end is setter method. It can be confusing.
  • @nobu (Nobuyoshi Nakada) also pointed out that the block is just ignored on the older Ruby versions.

Source of implementation and some discussions: https://github.com/ruby/ruby/pull/4793

Background:

I'm maintaining some default gems. They have copy of EnvUtil but I don't want to have it for maintainability. If Ruby provides this feature by default, we can use it instead of copying EnvUtil. I know that I need to implement/copy the feature in each default gem until Ruby 3.0 reaches EOL. But we don't have the feature now, I need to copy EnvUtil forever.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0