summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Daloze <eregontp@gmail.com>2020-09-17 15:15:43 +0200
committerBenoit Daloze <eregontp@gmail.com>2020-09-17 15:15:43 +0200
commit264889ec3d3d38fc1fd23c4fb48402f1367a8deb (patch)
tree17c72384804dc42aa85a417c9a721fdba90431d9
parentdeffb630210e35da146c3cee5972fb405b0f00b5 (diff)
Fix Mutex#unlock with a scheduler and thread contention
* It would hit "[BUG] unexpected THREAD_STOPPED" before.
-rw-r--r--test/fiber/test_mutex.rb23
-rw-r--r--thread_sync.c26
2 files changed, 36 insertions, 13 deletions
diff --git a/test/fiber/test_mutex.rb b/test/fiber/test_mutex.rb
index baec559a70..a70c6992ab 100644
--- a/test/fiber/test_mutex.rb
+++ b/test/fiber/test_mutex.rb
@@ -47,6 +47,29 @@ class TestFiberMutex < Test::Unit::TestCase
thread.join
end
+ def test_mutex_thread
+ mutex = Mutex.new
+ mutex.lock
+
+ thread = Thread.new do
+ scheduler = Scheduler.new
+ Thread.current.scheduler = scheduler
+
+ Fiber.schedule do
+ mutex.lock
+ sleep 0.1
+ mutex.unlock
+ end
+
+ scheduler.run
+ end
+
+ sleep 0.1
+ mutex.unlock
+
+ thread.join
+ end
+
def test_condition_variable
mutex = Mutex.new
condition = ConditionVariable.new
diff --git a/thread_sync.c b/thread_sync.c
index 741bff6160..10ff9c49b1 100644
--- a/thread_sync.c
+++ b/thread_sync.c
@@ -402,20 +402,20 @@ rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_fiber_t *fiber)
if (cur->th->scheduler != Qnil) {
rb_scheduler_unblock(cur->th->scheduler, cur->self, rb_fiberptr_self(cur->fiber));
+ } else {
+ switch (cur->th->status) {
+ case THREAD_RUNNABLE: /* from someone else calling Thread#run */
+ case THREAD_STOPPED_FOREVER: /* likely (rb_mutex_lock) */
+ rb_threadptr_interrupt(cur->th);
+ goto found;
+ case THREAD_STOPPED: /* probably impossible */
+ rb_bug("unexpected THREAD_STOPPED");
+ case THREAD_KILLED:
+ /* not sure about this, possible in exit GC? */
+ rb_bug("unexpected THREAD_KILLED");
+ continue;
+ }
}
-
- switch (cur->th->status) {
- case THREAD_RUNNABLE: /* from someone else calling Thread#run */
- case THREAD_STOPPED_FOREVER: /* likely (rb_mutex_lock) */
- rb_threadptr_interrupt(cur->th);
- goto found;
- case THREAD_STOPPED: /* probably impossible */
- rb_bug("unexpected THREAD_STOPPED");
- case THREAD_KILLED:
- /* not sure about this, possible in exit GC? */
- rb_bug("unexpected THREAD_KILLED");
- continue;
- }
}
found:
while (*th_mutex != mutex) {