Project

General

Profile

Actions

Backport #2103

closed

rev23993 causes Virtual Timer Expired when forking

Added by tmm1 (Aman Karmani) over 14 years ago. Updated almost 13 years ago.


Description

=begin
When forking, the thread timer is always started in the forked process, even though there are no threads running. The following patch by nobu should fix the issue:

diff --git a/eval.c b/eval.c
index 37c460d..d6359fe 100644
--- a/eval.c
+++ b/eval.c
@@ -12244,6 +12244,12 @@ rb_thread_alloc(klass)

static int thread_init;

+#if defined(POSIX_SIGNAL)
+#define CATCH_VTALRM() posix_signal(SIGVTALRM, catch_timer)
+#else
+#define CATCH_VTALRM() signal(SIGVTALRM, catch_timer)
+#endif
+
#if defined(_THREAD_SAFE)
static void
catch_timer(sig)
@@ -12327,6 +12333,8 @@ rb_thread_start_timer()
static pthread_cond_t start = PTHREAD_COND_INITIALIZER;

  if (thread_init) return;
  • if (rb_thread_alone()) return;

  • CATCH_VTALRM();
    args[0] = &time_thread;
    args[1] = &start;
    safe_mutex_lock(&time_thread.lock);
    @@ -12368,6 +12376,8 @@ rb_thread_start_timer()
    struct itimerval tval;

    if (thread_init) return;

  • if (rb_thread_alone()) return;

  • CATCH_VTALRM();
    tval.it_interval.tv_sec = 0;
    tval.it_interval.tv_usec = 10000;
    tval.it_value = tval.it_interval;
    @@ -12408,17 +12418,11 @@ rb_thread_start_0(fn, arg, th)
    "can't start a new thread (frozen ThreadGroup)");
    }

  • if (!thread_init) {
    #if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)
    -#if defined(POSIX_SIGNAL)
  • posix_signal(SIGVTALRM, catch_timer);
    -#else
  • signal(SIGVTALRM, catch_timer);
    -#endif
  • if (!thread_init) {
    rb_thread_start_timer();
    -#endif
    }
    +#endif

    if (THREAD_SAVE_CONTEXT(curr_thread)) {
    return thread;
    @@ -13167,6 +13171,9 @@ rb_thread_atfork()
    main_thread = curr_thread;
    curr_thread->next = curr_thread;
    curr_thread->prev = curr_thread;
    +#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)

  • rb_thread_stop_timer();
    +#endif
    }
    =end

Actions #1

Updated by tmm1 (Aman Karmani) over 14 years ago

=begin
Test script:

$ cat timer_test.rb
puts "parent: #{Process.pid}"
Process.fork do
puts "child: #{Process.pid}"
1_000_000.times{ 1+1 }
puts "child: done"
end
pid, status = Process.wait2
p status

Correct behavior with 1.8.6:

$ ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [x86_64-linux]

$ ruby timer_test.rb
parent: 16420
child: 16421
child: done
#<Process::Status: pid=16421,exited(0)>

Broken behavior with 1.8.7p202 on linux:

$ ruby -v
ruby 1.8.7 (2009-09-11 patchlevel 202) [x86_64-linux]

$ ruby timer_test.rb
parent: 16586
child: 16587
#<Process::Status: pid=16587,signaled(SIGVTALRM=26)>

=end

Actions #2

Updated by tmm1 (Aman Karmani) over 14 years ago

=begin
This bug causes issues with the mysql gem as well, which calls rb_thread_start_timer() from ext/mysql.c:

$ cat mysql_test.rb
require 'rubygems'
require 'mysql'
Mysql.connect('prudence', 'root')
1_000_000.times{ 1+1 }
puts 'done'


$ ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [x86_64-linux]

$ time ruby mysql_test.rb
done

real 0m0.616s
user 0m0.610s
sys 0m0.010s


$ ruby -v
ruby 1.8.7 (2009-09-11 patchlevel 202) [x86_64-linux]

$ time ruby mysql_test.rb
Virtual timer expired

real 0m0.106s
user 0m0.080s
sys 0m0.020s

=end

Actions #3

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

  • Category set to core
  • Status changed from Open to Closed

=begin
Applied in changeset r24958.
=end

Actions #4

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

  • Status changed from Closed to Assigned
  • Assignee changed from nobu (Nobuyoshi Nakada) to shyouhei (Shyouhei Urabe)

=begin

=end

Actions #5

Updated by tmm1 (Aman Karmani) over 14 years ago

=begin
The initial patch actually breaks the thread timer for the normal case, since rb_thread_start_timer() is called before the new thread is added to the doubly linked list.

diff --git a/eval.c b/eval.c
index f503cf6..9aad3d9 100644
--- a/eval.c
+++ b/eval.c
@@ -12608,6 +12608,9 @@ thread_insert(th)
curr_thread->next = th;
th->priority = curr_thread->priority;
th->thgroup = curr_thread->thgroup;
+#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)

  •   rb_thread_start_timer();
    

+#endif
}
}

@@ -12628,12 +12631,6 @@ rb_thread_start_0(fn, arg, th)
"can't start a new thread (frozen ThreadGroup)");
}

-#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)

  • if (!thread_init) {
  •   rb_thread_start_timer();
    
  • }
    -#endif
  • if (THREAD_SAVE_CONTEXT(curr_thread)) {
    return thread;
    }
    =end
Actions #6

Updated by shyouhei (Shyouhei Urabe) over 14 years ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

=begin
This issue was solved with changeset r25819.
Aman, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

=end

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0