summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2011-12-13 07:13:31 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2011-12-13 07:13:31 +0000
commitab6c8910f47a2b95f7338a182715ee0bee5ec45d (patch)
treed7691af9df1fbba665fc00009dea147af3a2eb31
parentddc15717ccb98d57a5b82b4138b1dc97375e4ac2 (diff)
* load.c (load_unlock): all threads requiring one file should
share same loading barrier, so it must be kept alive while those are waiting on it. [ruby-core:41618] [Bug #5754] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@34027 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog6
-rw-r--r--internal.h1
-rw-r--r--load.c8
-rw-r--r--test/ruby/test_require.rb56
-rw-r--r--thread.c11
5 files changed, 79 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index 85ad98662d..4d6dc47410 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Tue Dec 13 16:13:29 2011 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * load.c (load_unlock): all threads requiring one file should
+ share same loading barrier, so it must be kept alive while those
+ are waiting on it. [ruby-core:41618] [Bug #5754]
+
Tue Dec 13 07:30:14 2011 Aaron Patterson <aaron@tenderlovemaking.com>
* lib/webrick/httpresponse.rb (setup_header): 1xx responses
diff --git a/internal.h b/internal.h
index cf3fec20b5..b45b401c6f 100644
--- a/internal.h
+++ b/internal.h
@@ -181,6 +181,7 @@ void rb_thread_execute_interrupts(VALUE th);
void rb_clear_trace_func(void);
VALUE rb_thread_backtrace(VALUE thval);
VALUE rb_get_coverages(void);
+int rb_barrier_waiting(VALUE barrier);
/* thread_pthread.c, thread_win32.c */
void Init_native_thread(void);
diff --git a/load.c b/load.c
index 9f09dd548d..9d389f9efb 100644
--- a/load.c
+++ b/load.c
@@ -415,10 +415,12 @@ load_unlock(const char *ftptr, int done)
st_data_t key = (st_data_t)ftptr;
st_data_t data;
st_table *loading_tbl = get_loading_table();
+ VALUE barrier;
- if (st_delete(loading_tbl, &key, &data)) {
- VALUE barrier = (VALUE)data;
- xfree((char *)key);
+ if (!st_lookup(loading_tbl, key, &data)) return;
+ barrier = (VALUE)data;
+ if (rb_barrier_waiting(barrier) ||
+ (st_delete(loading_tbl, &key, &data) && (xfree((char *)key), 1))) {
if (done)
rb_barrier_destroy(barrier);
else
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index 96b1551da2..858ea6170a 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -339,4 +339,60 @@ class TestRequire < Test::Unit::TestCase
[], /\$LOADED_FEATURES is frozen; cannot append feature \(RuntimeError\)$/,
bug3756)
end
+
+ class << self
+ attr_accessor :scratch
+ end
+
+ def test_race_excption
+ bug5754 = '[ruby-core:41618]'
+ tmp = Tempfile.new(%w"bug5754 .rb")
+ path = tmp.path
+ tmp.print <<-EOS
+TestRequire.scratch << :pre
+Thread.pass until t2 = TestRequire.scratch[1]
+Thread.pass until t2.stop?
+open(__FILE__, "w") {|f| f.puts "TestRequire.scratch << :post"}
+raise "con1"
+ EOS
+ tmp.close
+
+ start = false
+ fin = false
+
+ TestRequire.scratch = scratch = []
+ t1_res = nil
+ t2_res = nil
+
+ t1 = Thread.new do
+ begin
+ require(path)
+ rescue RuntimeError
+ end
+
+ t1_res = require(path)
+
+ Thread.pass until fin
+ scratch << :t1
+ end
+
+ t2 = Thread.new do
+ Thread.pass until scratch[0]
+ begin
+ scratch << t2
+ t2_res = require(path)
+ scratch << :t2
+ ensure
+ fin = true
+ end
+ end
+
+ assert_nothing_raised(ThreadError, bug5754) {t1.join}
+ assert_nothing_raised(ThreadError, bug5754) {t2.join}
+
+ assert_equal([false, true], [t1_res, t2_res], bug5754)
+ assert_equal([:pre, t2, :post, :t2, :t1], scratch, bug5754)
+
+ tmp.close(true)
+ end
end
diff --git a/thread.c b/thread.c
index 2ad443807d..7165905007 100644
--- a/thread.c
+++ b/thread.c
@@ -3723,6 +3723,17 @@ rb_barrier_destroy(VALUE self)
return rb_mutex_unlock(mutex);
}
+int
+rb_barrier_waiting(VALUE self)
+{
+ VALUE mutex = GetBarrierPtr(self);
+ rb_mutex_t *m;
+
+ if (!mutex) return 0;
+ GetMutexPtr(mutex, m);
+ return m->cond_waiting;
+}
+
/* variables for recursive traversals */
static ID recursive_key;