[#34909] Kernel#evalの第2引数 — KURODA Hiraku <hiraku@...>
こんにちは、黒田ひらくと申します。
[#34911] erb still treats $KCODE — "Yusuke ENDOH" <mame@...>
遠藤です。
咳といいます。
まつもと ゆきひろです
永井@知能.九工大です.
[#34923] open() and encodings — "NARUSE, Yui" <naruse@...>
成瀬です。
まつもと ゆきひろです
成瀬です。
まつもと ゆきひろです
こんにちは、なかむら(う)です。
まつもと ゆきひろです
こんにちは、なかむら(う)です。
まつもと ゆきひろです
永井@知能.九工大です.
こんにちは、なかむら(う)です。
In article <4847146F.4020103@airemix.jp>,
永井@知能.九工大です.
成瀬です。
まつもと ゆきひろです
こんにちは、なかむら(う)です。
まつもと ゆきひろです
こんにちは、なかむら(う)です。
まつもと ゆきひろです
[#34938] Ruby1.8.7 で Rails2.0.2 が動かない — okkez <okkez000@...>
okkez です。
[#34979] [Bug:ruby 1.9] cannot open file with big mode value — "U.Nakamura" <usa@...>
こんにちは、なかむら(う)です。
[#34990] [Bug:1.9] Rational("355/113") clear $& — Tanaka Akira <akr@...>
以下のように、Rational("355/113") とすると $& が nil になり
> 以下のように、Rational("355/113") とすると $& が nil になり
ワナベと申します。
[#34997] Re: [ruby-changes:5517] Ruby:r17021 (trunk): * vm_insnhelper.c, vm.c, proc.c (proc_call): allow call method with — SASADA Koichi <ko1@...>
ささだです.
まつもと ゆきひろです
ささだです.
まつもと ゆきひろです
ささだです.
まつもと ゆきひろです
遠藤です。
まつもと ゆきひろです
遠藤です。
まつもと ゆきひろです
遠藤です。
ささだです.
ささだです.
まつもと ゆきひろです
ささだです.
[#35016] リリースプラン:1.9.0-2 — SASADA Koichi <ko1@...>
ささだです.
ささだです.
[#35020] Ruby 1.8.7-p17 has been released — "Akinori MUSHA" <knu@...>
Ruby 1.8.7-p17 をリリースしました。
pegacorn です。
[#35034] Array#shuffle! returns nil ? — okkez <okkez000@...>
okkez です。
[#35044] deadlock detection for 1.9 — "Yusuke ENDOH" <mame@...>
遠藤です。
こんばんは sheepman です。
まつもと ゆきひろです
[#35054] SSE2版 strlen — "NARUSE, Yui" <naruse@...>
成瀬です。
In message <485000F3.2010302@airemix.jp>
[#35056] Wide Finder performance — Yukihiro Matsumoto <matz@...>
まつもと ゆきひろです
ささだです.
[#35074] [Bug:1.9] irb fg — Kazuhiro NISHIYAMA <zn@...>
西山和広です。
[#35108] Re: [ruby-list:44988] Re: 各ブランチの計画 — Urabe Shyouhei <shyouhei@...>
卜部です。
こんにちは、なかむら(う)です。
[#35131] /\p{Hiragana}/s may dump core when sizeof(int) differs from sizeof(long) — "Yusuke ENDOH" <mame@...>
遠藤です。
[#35133] [BUG] class variable — Hidetoshi NAGAI <nagai@...>
永井@知能.九工大です.
ささだです.
まつもと ゆきひろです
ささだです.
まつもと ゆきひろです
[#35141] Etc::Passwd, Etc::Group というクラスは存在しない — okkez <okkez000@...>
okkez です。
[#35148] [Feature:trunk] IO#binmode? — "U.Nakamura" <usa@...>
こんにちは、なかむら(う)です。
[#35152] PATCH for MySQL/Ruby — Nobuyoshi Nakada <nobu@...>
なかだです。
[#35158] Etc.passwd{} のなかでのEtc.getpwnamの呼び出しがループします。 — "Yutaka Kanemoto" <kinpoco@...>
金本と申します。
[#35165] return from proc — SASADA Koichi <ko1@...>
ささだです.
[#35194] IncompatibleEncodingError — Kouhei Sutou <kou@...>
須藤です。
成瀬です。
須藤です。
[#35200] Win32 Unicode console output — Tietew <tietew@...>
Tietew です。
成瀬です。
artonです。
成瀬です。
成瀬です。
こんにちは、なかむら(う)です。
[#35226] [PATCH] freeze required_paths in gem_prelude.rb — "Keita Yamaguchi" <keita.yamaguchi@...>
山口と申します。
遠藤です。
まつもと ゆきひろです
卜部です。
まつもと ゆきひろです
卜部です。
まつもと ゆきひろです
[#35227] [Bug:trunk] Re: [ruby-cvs:24798] Ruby:r17573 (trunk): * parse.y (primary): make functional-style not operator to act — "U.Nakamura" <usa@...>
こんにちは、なかむら(う)です。
まつもと ゆきひろです
こんにちは、なかむら(う)です。
まつもと ゆきひろです
[#35247] Re: [ruby-list:45128] Re: Ruby 1.9.0/1.8.7/1.8.6/1.8.5 new releases (Security Fix) — Urabe Shyouhei <shyouhei@...>
卜部です。-devに振ります。ひょっとしてこんなパッチでSEGVのほうはおさまっ
まつもと ゆきひろです
前田です。
まつもと ゆきひろです
前田です。
卜部です。
[#35272] some insns should be method? — SASADA Koichi <ko1@...>
ささだです.
[#35273] $SAFEの今後 — Urabe Shyouhei <shyouhei@...>
〜これまでのあらすじ〜
まつもと ゆきひろです
卜部です。
まつもと ゆきひろです
卜部です。
まつもと ゆきひろです
前田です。
山口と申します。
[#35274] [Feature:trunk] test-coverage measurement — "Yusuke ENDOH" <mame@...>
遠藤です。
まつもと ゆきひろです
[#35278] [BUG] test_win32ole_event.rb in trunk — Masaki Suketa <masaki.suketa@...>
助田です。
こんにちは、なかむら(う)です。
artonです。
助田です。
artonです
助田です。
[ruby-dev:35044] deadlock detection for 1.9
遠藤です。
1.9 のスレッドにデッドロック検出を実装してみました。
$ ./ruby -e 'Thread.new { Thread.stop }; Thread.stop'
-e:1:in `stop': deadlock detected (fatal)
from -e:1:in `<main>'
ちょっとでかいパッチ (添付) ですが、要旨は以下の通りです。
- スレッドの状態種別に THREAD_STOPPED_FOREVER を追加。
無期限の sleep 状態と mutex 解放待ち状態を表す。
- rb_vm_t に変数 sleeper を追加。
THREAD_STOPPED_FOREVER 状態のスレッドの数を表す。
- rb_thread_t に変数 locking_mutex を追加。
このスレッドが待っている mutex を表す。
- rb_thread_t に変数 keeping_mutexes を追加。mutex_t に
変数 next_mutex を追加。
スレッドがロックしている mutex のリストを表す。
- mutex_t に変数 cond_notified を追加。
cond_signal されてまだ起動していないスレッドの数を表す。
- rb_mutex_lock や rb_mutex_unlock で上記の変数たちを
適宜更新するようにした。
- 唯一動いていそうなスレッドが native_cond_wait しそうな
時は、lock_func を抜けてデッドロック検査するようにした。
- rb_check_deadlock では vm->living_threads を列挙して、
- 例外状態のスレッド、または
- locking_mutex のロックに成功したスレッド、または
- locking_mutex が誰にもロックされていないスレッド、
のいずれもなければ main_thread に fatal な例外を投げる
ようにした。
- スレッドの終了時にロックしていた mutex をすべて解放
するようにした。
このパッチは元々 redmine の Feature #17 に流したものですが、
以下の修正を追加しています。
- thread_win32.c の修正を追加した (ただし未テスト)
- Kernel#sleep でデッドロック状態になった場合は例外にしない
ようにした
- バグフィクスとちょっと最適化
制限は以下の通りです。
- スレッドやロックの処理が少し重くなる。
計測のたびに変わるけれど、だいたいこのくらいです。
vm3_thread_create_join 6.565 6.586
vm3_thread_mutex 3.023 3.155
- スレッドがロック中の mutex が GC されなくなる。
loop { Mutex.new.lock } # どんどんメモリを消費する
- Thread#join 、#stop 、Mutex#lock 以外で固まっている
スレッドがあるとデッドロック検出しない。
r, w = IO.pipe; r.read # 固まり続ける
どんなもんでしょうか。
--
Yusuke ENDOH <mame@tsg.ne.jp>
Attachments (1)
Index: thread_win32.c
===================================================================
--- thread_win32.c (revision 17019)
+++ thread_win32.c (working copy)
@@ -204,7 +204,7 @@
}
static void
-native_sleep(rb_thread_t *th, struct timeval *tv)
+native_sleep(rb_thread_t *th, struct timeval *tv, int deadlockable)
{
DWORD msec;
if (tv) {
@@ -214,12 +214,19 @@
msec = INFINITE;
}
+ if (!tv && deadlockable) {
+ th->status = THREAD_STOPPED_FOREVER;
+ th->vm->sleeper++;
+ rb_check_deadlock(th->vm);
+ }
+ else {
+ th->status = THREAD_STOPPED;
+ }
GVL_UNLOCK_BEGIN();
{
DWORD ret;
int status = th->status;
- th->status = THREAD_STOPPED;
th->unblock.func = ubf_handle;
th->unblock.arg = th;
@@ -234,9 +241,10 @@
th->unblock.func = 0;
th->unblock.arg = 0;
- th->status = status;
}
GVL_UNLOCK_END();
+ th->status = status;
+ if (!tv && deadlockable) th->vm->sleeper++;
RUBY_VM_CHECK_INTS();
}
Index: thread_pthread.c
===================================================================
--- thread_pthread.c (revision 17019)
+++ thread_pthread.c (working copy)
@@ -402,7 +402,7 @@
#endif
static void
-native_sleep(rb_thread_t *th, struct timeval *tv)
+native_sleep(rb_thread_t *th, struct timeval *tv, int deadlockable)
{
int prev_status = th->status;
struct timespec ts;
@@ -418,7 +418,14 @@
}
}
- th->status = THREAD_STOPPED;
+ if (!tv && deadlockable) {
+ th->status = THREAD_STOPPED_FOREVER;
+ th->vm->sleeper++;
+ rb_check_deadlock(th->vm);
+ }
+ else {
+ th->status = THREAD_STOPPED;
+ }
thread_debug("native_sleep %ld\n", tv ? tv->tv_sec : -1);
GVL_UNLOCK_BEGIN();
@@ -455,9 +462,10 @@
th->unblock.arg = 0;
pthread_mutex_unlock(&th->interrupt_lock);
- th->status = prev_status;
}
GVL_UNLOCK_END();
+ th->status = prev_status;
+ if (!tv && deadlockable) th->vm->sleeper--;
RUBY_VM_CHECK_INTS();
thread_debug("native_sleep done\n");
Index: bootstraptest/test_thread.rb
===================================================================
--- bootstraptest/test_thread.rb (revision 17019)
+++ bootstraptest/test_thread.rb (working copy)
@@ -268,3 +268,55 @@
at_exit { Fiber.new{}.resume }
}
+assert_equal 'ok', %q{
+ begin
+ Thread.new { Thread.stop }
+ Thread.stop
+ :ng
+ rescue Exception
+ :ok
+ end
+}
+
+assert_equal 'ok', %q{
+ begin
+ m1, m2 = Mutex.new, Mutex.new
+ Thread.new { m1.lock; sleep 1; m2.lock }
+ m2.lock; sleep 1; m1.lock
+ :ng
+ rescue Exception
+ :ok
+ end
+}
+
+assert_equal 'ok', %q{
+ m = Mutex.new
+ Thread.new { m.lock }; sleep 1; m.lock
+ :ok
+}
+
+assert_equal 'ok', %q{
+ m = Mutex.new
+ Thread.new { m.lock }; m.lock
+ :ok
+}
+
+assert_equal 'ok', %q{
+ m = Mutex.new
+ Thread.new { m.lock }.join; m.lock
+ :ok
+}
+
+assert_equal 'ok', %q{
+ m = Mutex.new
+ Thread.new { m.lock; sleep 2 }
+ sleep 1; m.lock
+ :ok
+}
+
+assert_equal 'ok', %q{
+ m = Mutex.new
+ Thread.new { m.lock; sleep 2; m.unlock }
+ sleep 1; m.lock
+ :ok
+}
Index: vm_core.h
===================================================================
--- vm_core.h (revision 17019)
+++ vm_core.h (working copy)
@@ -302,6 +302,7 @@
int running;
int thread_abort_on_exception;
unsigned long trace_flag;
+ volatile int sleeper;
/* object management */
VALUE mark_object_ary;
@@ -356,6 +357,7 @@
THREAD_TO_KILL,
THREAD_RUNNABLE,
THREAD_STOPPED,
+ THREAD_STOPPED_FOREVER,
THREAD_KILLED,
};
@@ -425,6 +427,8 @@
int interrupt_flag;
rb_thread_lock_t interrupt_lock;
struct rb_unblock_callback unblock;
+ VALUE locking_mutex;
+ VALUE keeping_mutexes;
struct rb_vm_tag *tag;
struct rb_vm_trap_tag *trap_tag;
Index: thread.c
===================================================================
--- thread.c (revision 17019)
+++ thread.c (working copy)
@@ -57,11 +57,14 @@
static void sleep_timeval(rb_thread_t *th, struct timeval time);
static void sleep_wait_for_interrupt(rb_thread_t *th, double sleepsec);
-static void sleep_forever(rb_thread_t *th);
+static void sleep_forever(rb_thread_t *th, int nodeadlock);
static double timeofday(void);
struct timeval rb_time_interval(VALUE);
static int rb_thread_dead(rb_thread_t *th);
+static void rb_mutex_unlock_all(VALUE);
+static void rb_check_deadlock(rb_vm_t *vm);
+
void rb_signal_exec(rb_thread_t *th, int sig);
void rb_disable_interrupt(void);
@@ -93,12 +96,12 @@
rb_thread_set_current(_th_stored); \
} while(0)
-#define BLOCKING_REGION(exec, ubf, ubfarg) do { \
+#define BLOCKING_REGION(exec, ubf, ubfarg, stopped) do { \
rb_thread_t *__th = GET_THREAD(); \
int __prev_status = __th->status; \
struct rb_unblock_callback __oldubf; \
set_unblock_function(__th, ubf, ubfarg, &__oldubf); \
- __th->status = THREAD_STOPPED; \
+ if (stopped) __th->status = THREAD_STOPPED; \
thread_debug("enter blocking region (%p)\n", __th); \
GVL_UNLOCK_BEGIN(); {\
exec; \
@@ -107,10 +110,9 @@
thread_debug("leave blocking region (%p)\n", __th); \
remove_signal_thread_list(__th); \
reset_unblock_function(__th, &__oldubf); \
- if (__th->status == THREAD_STOPPED) { \
+ if (stopped && __th->status == THREAD_STOPPED) { \
__th->status = __prev_status; \
} \
- RUBY_VM_CHECK_INTS(); \
} while(0)
#if THREAD_DEBUG
@@ -263,6 +265,11 @@
rb_bug("rb_thread_terminate_all: called by child thread (%p, %p)", vm->main_thread, th);
}
+ /* unlock all locking mutexes */
+ if (th->keeping_mutexes) {
+ rb_mutex_unlock_all(th->keeping_mutexes);
+ }
+
thread_debug("rb_thread_terminate_all (main thread: %p)\n", th);
st_foreach(vm->living_threads, terminate_i, (st_data_t)th);
@@ -361,6 +368,17 @@
}
TH_POP_TAG();
+ /* locking_mutex must be Qfalse */
+ if (th->locking_mutex != Qfalse) {
+ rb_bug("thread_start_func_2: locking_mutex must be NULL (%p:%ld)", th, th->locking_mutex);
+ }
+
+ /* unlock all locking mutexes */
+ if (th->keeping_mutexes) {
+ rb_mutex_unlock_all(th->keeping_mutexes);
+ }
+
+ /* delete self from living_threads */
st_delete_wrap(th->vm->living_threads, th->self);
/* wake up joinning threads */
@@ -371,6 +389,7 @@
join_th = join_th->join_list_next;
}
st_delete_wrap(th->vm->living_threads, th->self);
+ if (th != main_th) rb_check_deadlock(th->vm);
if (!th->root_fiber) {
rb_thread_recycle_stack_release(th->stack);
@@ -511,7 +530,7 @@
while (target_th->status != THREAD_KILLED) {
if (p->forever) {
- sleep_forever(th);
+ sleep_forever(th, 1);
}
else {
now = timeofday();
@@ -667,24 +686,31 @@
}
static void
-sleep_forever(rb_thread_t *th)
+sleep_forever(rb_thread_t *th, int deadlockable)
{
- native_sleep(th, 0);
+ native_sleep(th, 0, deadlockable);
}
static void
sleep_timeval(rb_thread_t *th, struct timeval tv)
{
- native_sleep(th, &tv);
+ native_sleep(th, &tv, 0);
}
void
rb_thread_sleep_forever()
{
thread_debug("rb_thread_sleep_forever\n");
- sleep_forever(GET_THREAD());
+ sleep_forever(GET_THREAD(), 0);
}
+static void
+rb_thread_sleep_deadly()
+{
+ thread_debug("rb_thread_sleep_deadly\n");
+ sleep_forever(GET_THREAD(), 1);
+}
+
static double
timeofday(void)
{
@@ -782,7 +808,8 @@
BLOCKING_REGION({
val = func(data1);
- }, ubf, data2);
+ }, ubf, data2, 1);
+ RUBY_VM_CHECK_INTS();
return val;
}
@@ -1128,7 +1155,7 @@
rb_raise(rb_eThreadError,
"stopping only thread\n\tnote: use sleep to stop forever");
}
- rb_thread_sleep_forever();
+ rb_thread_sleep_deadly();
return Qnil;
}
@@ -1142,6 +1169,7 @@
switch (th->status) {
case THREAD_RUNNABLE:
case THREAD_STOPPED:
+ case THREAD_STOPPED_FOREVER:
case THREAD_TO_KILL:
rb_ary_push(ary, th->self);
default:
@@ -1336,6 +1364,7 @@
case THREAD_RUNNABLE:
return "run";
case THREAD_STOPPED:
+ case THREAD_STOPPED_FOREVER:
return "sleep";
case THREAD_TO_KILL:
return "aborting";
@@ -1435,7 +1464,7 @@
if (rb_thread_dead(th))
return Qtrue;
- if (th->status == THREAD_STOPPED)
+ if (th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER)
return Qtrue;
return Qfalse;
}
@@ -1875,14 +1904,16 @@
if (except) *except = orig_except;
wait = &wait_100ms;
} while (__th->interrupt_flag == 0 && (timeout == 0 || subst(timeout, &wait_100ms)));
- }, 0, 0);
+ }, 0, 0, 1);
+ RUBY_VM_CHECK_INTS();
} while (result == 0 && (timeout == 0 || subst(timeout, &wait_100ms)));
}
#else
BLOCKING_REGION({
result = select(n, read, write, except, timeout);
if (result < 0) lerrno = errno;
- }, ubf_select, GET_THREAD());
+ }, ubf_select, GET_THREAD(), 1);
+ RUBY_VM_CHECK_INTS();
#endif
errno = lerrno;
@@ -2319,7 +2350,8 @@
rb_thread_lock_t lock;
rb_thread_cond_t cond;
rb_thread_t volatile *th;
- volatile int cond_waiting;
+ volatile int cond_waiting, cond_notified;
+ VALUE next_mutex;
} mutex_t;
#define GetMutexPtr(obj, tobj) \
@@ -2333,6 +2365,9 @@
if (mutex->th) {
rb_gc_mark(mutex->th->self);
}
+ if (mutex->next_mutex) {
+ rb_gc_mark(mutex->next_mutex);
+ }
}
}
@@ -2391,6 +2426,17 @@
return mutex->th ? Qtrue : Qfalse;
}
+static void
+mutex_locked(rb_thread_t *th, VALUE self)
+{
+ if (th->keeping_mutexes) {
+ mutex_t *mutex;
+ GetMutexPtr(self, mutex);
+ mutex->next_mutex = th->keeping_mutexes;
+ }
+ th->keeping_mutexes = self;
+}
+
/*
* call-seq:
* mutex.try_lock => true or false
@@ -2413,6 +2459,8 @@
if (mutex->th == 0) {
mutex->th = GET_THREAD();
locked = Qtrue;
+
+ mutex_locked(GET_THREAD(), self);
}
native_mutex_unlock(&mutex->lock);
@@ -2420,17 +2468,23 @@
}
static int
-lock_func(rb_thread_t *th, mutex_t *mutex)
+lock_func(rb_thread_t *th, mutex_t *mutex, int last_thread)
{
- int interrupted = Qfalse;
+ int interrupted = 0;
native_mutex_lock(&mutex->lock);
while (mutex->th || (mutex->th = th, 0)) {
+ if (last_thread) {
+ interrupted = 2;
+ break;
+ }
+
mutex->cond_waiting++;
native_cond_wait(&mutex->cond, &mutex->lock);
+ mutex->cond_notified--;
- if (th->interrupt_flag) {
- interrupted = Qtrue;
+ if (RUBY_VM_INTERRUPTED(th)) {
+ interrupted = 1;
break;
}
}
@@ -2445,6 +2499,7 @@
native_mutex_lock(&mutex->lock);
if (mutex->cond_waiting > 0) {
native_cond_broadcast(&mutex->cond);
+ mutex->cond_notified = mutex->cond_waiting;
mutex->cond_waiting = 0;
}
native_mutex_unlock(&mutex->lock);
@@ -2467,11 +2522,30 @@
while (mutex->th != th) {
int interrupted;
+ int prev_status = th->status;
+ int last_thread = 0;
+ th->locking_mutex = self;
+ th->status = THREAD_STOPPED_FOREVER;
+ th->vm->sleeper++;
+ if (th->vm->living_threads->num_entries == th->vm->sleeper) {
+ last_thread = 1;
+ }
+
BLOCKING_REGION({
- interrupted = lock_func(th, mutex);
- }, lock_interrupt, mutex);
+ interrupted = lock_func(th, mutex, last_thread);
+ }, lock_interrupt, mutex, 0);
+ th->locking_mutex = Qfalse;
+ if (interrupted == 2) {
+ rb_check_deadlock(th->vm);
+ RUBY_VM_SET_TIMER_INTERRUPT(th);
+ }
+ th->status = prev_status;
+ th->vm->sleeper--;
+
+ if (mutex->th == th) mutex_locked(th, self);
+
if (interrupted) {
RUBY_VM_CHECK_INTS();
}
@@ -2480,15 +2554,8 @@
return self;
}
-/*
- * call-seq:
- * mutex.unlock => self
- *
- * Releases the lock.
- * Raises +ThreadError+ if +mutex+ wasn't locked by the current thread.
- */
-VALUE
-rb_mutex_unlock(VALUE self)
+static const char *
+mutex_unlock(VALUE self)
{
mutex_t *mutex;
const char *err = NULL;
@@ -2513,15 +2580,61 @@
native_mutex_unlock(&mutex->lock);
+ return err;
+}
+
+/*
+ * call-seq:
+ * mutex.unlock => self
+ *
+ * Releases the lock.
+ * Raises +ThreadError+ if +mutex+ wasn't locked by the current thread.
+ */
+VALUE
+rb_mutex_unlock(VALUE self)
+{
+ const char *err;
+ rb_thread_t *th = GET_THREAD();
+ VALUE mtxval = th->keeping_mutexes;
+ mutex_t *mutex, *mutex2;
+
+ err = mutex_unlock(self);
if (err) rb_raise(rb_eThreadError, err);
+ GetMutexPtr(mtxval, mutex);
+ if (mtxval == self) {
+ th->keeping_mutexes = mutex->next_mutex;
+ }
+ else {
+ while (mutex->next_mutex != self) {
+ mtxval = mutex->next_mutex;
+ GetMutexPtr(mtxval, mutex);
+ }
+ mtxval = mutex->next_mutex;
+ GetMutexPtr(mtxval, mutex2);
+ mutex->next_mutex = mutex2->next_mutex;
+ }
return self;
}
+static void
+rb_mutex_unlock_all(VALUE mutexes)
+{
+ const char *err;
+ mutex_t *mutex;
+
+ while (mutexes) {
+ err = mutex_unlock(mutexes);
+ if (err) rb_bug("invalid keeping_mutexes");
+ GetMutexPtr(mutexes, mutex);
+ mutexes = mutex->next_mutex;
+ }
+}
+
static VALUE
rb_mutex_sleep_forever(VALUE time)
{
- rb_thread_sleep_forever();
+ rb_thread_sleep_deadly();
return Qnil;
}
@@ -3268,3 +3381,48 @@
return th ? Qtrue : Qfalse;
}
+
+static int
+check_deadlock_i(st_data_t key, st_data_t val, int *found)
+{
+ VALUE thval = key;
+ rb_thread_t *th;
+ GetThreadPtr(thval, th);
+
+ if (th->status != THREAD_STOPPED_FOREVER) {
+ rb_bug("check_deadlock_i: thread that is not THREAD_STOPPED_FOREVER found (%p:%d)", th, th->status);
+ }
+
+ if (RUBY_VM_INTERRUPTED(th)) {
+ *found = 1;
+ }
+ else if (th->locking_mutex) {
+ mutex_t *mutex;
+ GetMutexPtr(th->locking_mutex, mutex);
+
+ native_mutex_lock(&mutex->lock);
+ if (mutex->th == th || (!mutex->th && mutex->cond_notified)) {
+ *found = 1;
+ }
+ native_mutex_unlock(&mutex->lock);
+ }
+
+ return (*found) ? ST_STOP : ST_CONTINUE;
+}
+
+static void
+rb_check_deadlock(rb_vm_t *vm)
+{
+ int found = 0;
+
+ if (vm->living_threads->num_entries != vm->sleeper) return;
+
+ st_foreach(vm->living_threads, check_deadlock_i, (st_data_t)&found);
+
+ if (!found) {
+ VALUE argv[2];
+ argv[0] = rb_eFatal;
+ argv[1] = rb_str_new2("deadlock detected");
+ rb_thread_raise(2, argv, vm->main_thread);
+ }
+}
Index: vm.c
===================================================================
--- vm.c (revision 17019)
+++ vm.c (working copy)
@@ -1445,6 +1445,13 @@
RUBY_FREE_UNLESS_NULL(th->stack);
}
+ if (th->locking_mutex != Qfalse) {
+ rb_bug("thread_free: locking_mutex must be NULL (%p:%ld)", th, th->locking_mutex);
+ }
+ if (th->keeping_mutexes != Qfalse) {
+ rb_bug("thread_free: keeping_mutexes must be NULL (%p:%ld)", th, th->locking_mutex);
+ }
+
if (th->local_storage) {
st_free_table(th->local_storage);
}
@@ -1512,6 +1519,9 @@
RUBY_MARK_UNLESS_NULL(th->root_fiber);
RUBY_MARK_UNLESS_NULL(th->stat_insn_usage);
+ RUBY_MARK_UNLESS_NULL(th->locking_mutex);
+ RUBY_MARK_UNLESS_NULL(th->keeping_mutexes);
+
rb_mark_tbl(th->local_storage);
if (GET_THREAD() != th && th->machine_stack_start && th->machine_stack_end) {