
Issue #20197 has been updated by byroot (Jean Boussier).
especially for profilers such as StackProf.
Not that it makes this less of a bug, but note that recent versions of StackProf (and more mordern profilers like Vernier) avoid relying on postponed jobs, because no matter how frequent they may be triggered, that always give biased results. Ref: https://github.com/tmm1/stackprof/blob/ebdd3af48a2c4ddf35b0a73858ea72dcdb551... ---------------------------------------- Bug #20197: Postponed job invocations are significantly reduced in Ruby 3.3 https://bugs.ruby-lang.org/issues/20197#change-106376 * Author: osyoyu (Daisuke Aritomo) * Status: Open * Priority: Normal * Assignee: kjtsanaktsidis (KJ Tsanaktsidis) * ruby -v: ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux] * Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN ---------------------------------------- The number of postponed job invocations has been significantly reduced in Ruby 3.3. While my understanding is that postponed jobs provide no guarantee of how soon registered callbacks will fire, I believe the current rate is too low for practical usage, especially for profilers such as StackProf. A git bisect led me to https://github.com/ruby/ruby/commit/1f0304218cf00e05a4a126196676ba221ebf91f6 which obviously seems to be related, but I'm not sure why. ## Repro ### Expected The job fires (nearly) immediately after being triggered. In the following example, the job is triggered every 100 ms. ``` % ruby bin/test.rb # runs for 3 seconds count: 1 count: 2 (snip) count: 29 ``` ### Actual The job gets fired only once. ``` % ruby bin/test.rb count: 1 ``` ### Code ```ruby require 'mycext' time = Time.now th = Thread.new do loop do sleep 0.01 break if Time.now - time > 3 # run for 3 seconds end end th.join ``` ```c #include <pthread.h> #include <stdio.h> #include <time.h> #include "ruby.h" #include "ruby/debug.h" int called_count; void postponed_job(void *ptr) { called_count++; printf("count: %d\n", called_count); } _Noreturn void * pthread_main(void *_) { while (1) { rb_postponed_job_register_one(0, postponed_job, NULL); // Sleep for 100 ms struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 100 * 1000 * 1000; nanosleep(&ts, NULL); } } RUBY_FUNC_EXPORTED void Init_mycext(void) { called_count = 0; pthread_t pthread; pthread_create(&pthread, NULL, pthread_main, NULL); } ``` -- https://bugs.ruby-lang.org/