Backport #2103
closedrev23993 causes Virtual Timer Expired when forking
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
}
+#endifif (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
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
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
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
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
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
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