diff --git a/lib/syntax_suggest/core_ext.rb b/lib/syntax_suggest/core_ext.rb index ffbc922eedf172..3918c45d1edbab 100644 --- a/lib/syntax_suggest/core_ext.rb +++ b/lib/syntax_suggest/core_ext.rb @@ -33,8 +33,8 @@ def detailed_message(highlight: true, syntax_suggest: true, **kwargs) end rescue => e if ENV["SYNTAX_SUGGEST_DEBUG"] - $stderr.warn(e.message) - $stderr.warn(e.backtrace) + warn(e.message) + warn(e.backtrace) end # Ignore internal errors diff --git a/process.c b/process.c index a94b1b4fced775..a31843f9314e81 100644 --- a/process.c +++ b/process.c @@ -8124,6 +8124,13 @@ ruby_real_ms_time(void) * +:CLOCK_MONOTONIC+, +:CLOCK_PROCESS_CPUTIME_ID+, * and +:CLOCK_THREAD_CPUTIME_ID+ are optional. * + * +:CLOCK_THREAD_CPUTIME_ID+ measures the native thread that reads it, not + * the Ruby thread. Under the M:N thread scheduler (see +RUBY_MN_THREADS+) a + * Ruby thread can move between native threads, so two reads taken from one + * Ruby thread may come from different native threads, and the later read can + * be smaller than the earlier one. To measure elapsed time there, use + * +:CLOCK_PROCESS_CPUTIME_ID+ or +:CLOCK_MONOTONIC+. + * * Certain emulations are used when the given +clock_id+ * is not supported directly: * diff --git a/spec/syntax_suggest/integration/ruby_command_line_spec.rb b/spec/syntax_suggest/integration/ruby_command_line_spec.rb index 02354ceff00eea..761bd760b5d56a 100644 --- a/spec/syntax_suggest/integration/ruby_command_line_spec.rb +++ b/spec/syntax_suggest/integration/ruby_command_line_spec.rb @@ -185,5 +185,34 @@ class Dog expect(out).to include("Invalid break") end end + + it "SYNTAX_SUGGEST_DEBUG reports a rescued internal error instead of masking it" do + Dir.mktmpdir do |dir| + tmpdir = Pathname(dir) + + # Force `SyntaxSuggest.call` to raise so the rescue in `detailed_message` fires + monkeypatch = tmpdir.join("raise_monkeypatch.rb") + monkeypatch.write <<~EOM + require "syntax_suggest/api" + + module SyntaxSuggest + def self.call(*args, **kwargs) + raise "boom from monkeypatch" + end + end + EOM + + script = tmpdir.join("script.rb") + script.write <<~EOM + def lol + puts "haha" + EOM + + out = `SYNTAX_SUGGEST_DEBUG=1 #{ruby} -I#{lib_dir} -rsyntax_suggest -r#{monkeypatch} #{script} 2>&1` + + expect($?.success?).to be_falsey + expect(out).to include("boom from monkeypatch") + end + end end end diff --git a/thread.c b/thread.c index b034522ea4adcd..ebaf61d963e075 100644 --- a/thread.c +++ b/thread.c @@ -5272,6 +5272,7 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r rb_native_mutex_initialize(&th->interrupt_lock); rb_native_mutex_initialize(&vm->once_lock); rb_native_cond_initialize(&vm->once_cond); + rb_jit_cont_init(); // cont.c's jit_cont_lock, likewise rb_gc_zombie_objspaces_atfork(); rb_gc_atfork_global_locks(); rb_generic_fields_lock_atfork(); diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 8b229a25486c19..002c367e94b8e7 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -876,10 +876,15 @@ def assert_all_assertions_foreach(msg = nil, *keys, &block) end alias all_assertions_foreach assert_all_assertions_foreach - %w[ + clocks = %w[ CLOCK_THREAD_CPUTIME_ID CLOCK_PROCESS_CPUTIME_ID CLOCK_MONOTONIC - ].find do |c| + ] + # CLOCK_THREAD_CPUTIME_ID is per native thread, and an M:N thread moves + # between native threads, so it can go backwards and yield a negative + # elapsed time. Measure process CPU time there instead. + clocks.shift if RUBY_DESCRIPTION.include?("+MN") + clocks.find do |c| if Process.const_defined?(c) [c.to_sym, Process.const_get(c)].find do |clk| begin