diff --git a/lib/vernier.rb b/lib/vernier.rb index cb085722..c0fdd6b5 100644 --- a/lib/vernier.rb +++ b/lib/vernier.rb @@ -5,6 +5,7 @@ require_relative "vernier/stack_table" require_relative "vernier/result" require_relative "vernier/hooks" +require_relative "vernier/fork" require_relative "vernier/vernier" require_relative "vernier/output/firefox" require_relative "vernier/output/top" @@ -56,6 +57,11 @@ def self.stop_profile result end + def self.cancel_profile + @collector&.cancel + @collector = nil + end + def self.trace_retained(**profile_options, &block) profile(**profile_options.merge(mode: :retained), &block) end diff --git a/lib/vernier/collector.rb b/lib/vernier/collector.rb index d9e695b1..6022c0c0 100644 --- a/lib/vernier/collector.rb +++ b/lib/vernier/collector.rb @@ -71,6 +71,15 @@ def record_interval(category, name = category) ) end + def cancel + finish + @thread_names.cancel + @hooks.each do |hook| + hook.disable + end + nil + end + def stop result = finish diff --git a/lib/vernier/fork.rb b/lib/vernier/fork.rb new file mode 100644 index 00000000..c8e8ca23 --- /dev/null +++ b/lib/vernier/fork.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Vernier + if ::Process.respond_to?(:_fork) + module ForkHooks + def _fork + pid = super + if pid == 0 # We're in the child + Vernier.cancel_profile + end + pid + end + end + + ::Process.singleton_class.prepend(ForkHooks) + end +end diff --git a/lib/vernier/thread_names.rb b/lib/vernier/thread_names.rb index 2036424a..6ff45619 100644 --- a/lib/vernier/thread_names.rb +++ b/lib/vernier/thread_names.rb @@ -13,6 +13,10 @@ def [](object_id) @names[object_id] || "thread obj_id:#{object_id}" end + def cancel + @tp.disable + end + def finish collect_running @tp.disable diff --git a/test/test_vernier.rb b/test/test_vernier.rb index 6d766af5..8bb6542d 100644 --- a/test/test_vernier.rb +++ b/test/test_vernier.rb @@ -6,4 +6,12 @@ class TestVernier < Minitest::Test def test_that_it_has_a_version_number refute_nil ::Vernier::VERSION end + + def test_that_forked_children_do_not_hang + pid = Process.fork do + # noop + end + _, status = Process.waitpid2(pid) + assert_predicate status, :success? + end end