Skip to content

Commit 39d1961

Browse files
authored
Merge pull request mono#9476 from lateralusX/lateralusX/issue8708
Fix race condition in WaitAnyWithSecondMutexAbandoned test.
2 parents abcf884 + b3a524e commit 39d1961

File tree

2 files changed

+84
-19
lines changed

2 files changed

+84
-19
lines changed

mcs/class/corlib/Test/System.Threading/WaitHandleTest.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ public void WaitOneWithAbandonedMutex ()
409409
m.WaitOne ();
410410
});
411411
thread1.Start ();
412-
thread1.Join (1000);
412+
Assert.IsTrue (thread1.Join (Timeout.Infinite), "thread1.Join");
413413
try {
414414
m.WaitOne ();
415415
Assert.Fail ("Expected AbandonedMutexException");
@@ -421,7 +421,7 @@ public void WaitOneWithAbandonedMutex ()
421421
signalled = m.WaitOne (100);
422422
});
423423
thread2.Start ();
424-
thread2.Join (1000);
424+
Assert.IsTrue (thread2.Join (Timeout.Infinite), "thread2.Join");
425425
Assert.IsFalse (signalled);
426426

427427
// Since this thread owns the Mutex releasing it shouldn't fail
@@ -489,7 +489,7 @@ public void WaitAnyWithSecondMutexAbandoned ()
489489
m1.ReleaseMutex ();
490490
});
491491
thread1.Start ();
492-
thread1.Join (1000);
492+
Assert.IsTrue (thread1.Join (Timeout.Infinite), "thread1.Join");
493493
thread2.Start ();
494494
while (!mainProceed) {
495495
Thread.Sleep (10);
@@ -502,7 +502,7 @@ public void WaitAnyWithSecondMutexAbandoned ()
502502
Assert.AreEqual (m2, e.Mutex);
503503
} finally {
504504
thread2Proceed = true;
505-
thread2.Join (1000);
505+
Assert.IsTrue (thread2.Join (Timeout.Infinite), "thread2.Join");
506506
}
507507

508508
// Current thread should own the second Mutex now
@@ -511,7 +511,7 @@ public void WaitAnyWithSecondMutexAbandoned ()
511511
signalled = WaitHandle.WaitAny (new WaitHandle [] { m1, m2 }, 0);
512512
});
513513
thread3.Start ();
514-
thread3.Join (1000);
514+
Assert.IsTrue (thread3.Join (Timeout.Infinite), "thread3.Join");
515515
Assert.AreEqual (0, signalled);
516516

517517
// Since this thread owns the second Mutex releasing it shouldn't fail
@@ -540,7 +540,7 @@ public void WaitAllWithOneAbandonedMutex ()
540540
m1.WaitOne ();
541541
});
542542
thread.Start ();
543-
thread.Join (1000);
543+
Assert.IsTrue (thread.Join (Timeout.Infinite), "thread.Join");
544544
WaitHandle.WaitAll (new WaitHandle [] { m1, m2 });
545545
}
546546
}

mono/metadata/threads.c

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ static void mono_threads_unlock (void);
127127
static MonoCoopMutex threads_mutex;
128128

129129
/* Controls access to the 'joinable_threads' hash table */
130-
#define joinable_threads_lock() mono_os_mutex_lock (&joinable_threads_mutex)
131-
#define joinable_threads_unlock() mono_os_mutex_unlock (&joinable_threads_mutex)
132-
static mono_mutex_t joinable_threads_mutex;
130+
#define joinable_threads_lock() mono_coop_mutex_lock (&joinable_threads_mutex)
131+
#define joinable_threads_unlock() mono_coop_mutex_unlock (&joinable_threads_mutex)
132+
static MonoCoopMutex joinable_threads_mutex;
133133

134134
/* Holds current status of static data heap */
135135
static StaticDataInfo thread_static_info;
@@ -163,10 +163,19 @@ static MonoGHashTable *threads_starting_up = NULL;
163163
static GHashTable *joinable_threads;
164164
static gint32 joinable_thread_count;
165165

166+
/* mono_threads_join_threads will take threads from joinable_threads list and wait for them. */
167+
/* When this happens, the tid is not on the list anymore so mono_thread_join assumes the thread has complete */
168+
/* and will return back to the caller. This could cause a race since caller of join assumes thread has completed */
169+
/* and on some OS it could cause errors. Keeping the tid's currently pending a native thread join call */
170+
/* in a separate table (only affecting callers interested in this internal join detail) and look at that table in mono_thread_join */
171+
/* will close this race. */
172+
static GHashTable *pending_native_thread_join_calls;
173+
static MonoCoopCond pending_native_thread_join_calls_event;
174+
166175
static GHashTable *pending_joinable_threads;
167176
static gint32 pending_joinable_thread_count;
168177

169-
static mono_cond_t zero_pending_joinable_thread_event;
178+
static MonoCoopCond zero_pending_joinable_thread_event;
170179

171180
static void threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info);
172181
static gboolean threads_wait_pending_joinable_threads (uint32_t timeout);
@@ -3184,11 +3193,12 @@ void mono_thread_init (MonoThreadStartCB start_cb,
31843193
mono_coop_mutex_init_recursive (&threads_mutex);
31853194

31863195
mono_os_mutex_init_recursive(&interlocked_mutex);
3187-
mono_os_mutex_init_recursive(&joinable_threads_mutex);
3196+
mono_coop_mutex_init_recursive(&joinable_threads_mutex);
31883197

31893198
mono_os_event_init (&background_change_event, FALSE);
31903199

3191-
mono_os_cond_init (&zero_pending_joinable_thread_event);
3200+
mono_coop_cond_init (&pending_native_thread_join_calls_event);
3201+
mono_coop_cond_init (&zero_pending_joinable_thread_event);
31923202

31933203
mono_init_static_data_info (&thread_static_info);
31943204
mono_init_static_data_info (&context_static_info);
@@ -3325,7 +3335,8 @@ mono_thread_cleanup (void)
33253335
mono_os_mutex_destroy (&interlocked_mutex);
33263336
mono_os_mutex_destroy (&delayed_free_table_mutex);
33273337
mono_os_mutex_destroy (&small_id_mutex);
3328-
mono_os_cond_destroy (&zero_pending_joinable_runtime_thread_event);
3338+
mono_coop_cond_destroy (&zero_pending_joinable_thread_event);
3339+
mono_coop_cond_destroy (&pending_native_thread_join_calls_event);
33293340
mono_os_event_destroy (&background_change_event);
33303341
#endif
33313342
}
@@ -5594,7 +5605,7 @@ threads_remove_pending_joinable_thread_nolock (gpointer tid)
55945605
if (pending_joinable_threads && g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
55955606
g_hash_table_remove (pending_joinable_threads, tid);
55965607
if (UnlockedDecrement (&pending_joinable_thread_count) == 0)
5597-
mono_os_cond_broadcast (&zero_pending_joinable_thread_event);
5608+
mono_coop_cond_broadcast (&zero_pending_joinable_thread_event);
55985609
}
55995610
}
56005611

@@ -5605,12 +5616,12 @@ threads_wait_pending_joinable_threads (uint32_t timeout)
56055616
joinable_threads_lock ();
56065617
if (timeout == MONO_INFINITE_WAIT) {
56075618
while (UnlockedRead (&pending_joinable_thread_count) > 0)
5608-
mono_os_cond_wait (&zero_pending_joinable_thread_event, &joinable_threads_mutex);
5619+
mono_coop_cond_wait (&zero_pending_joinable_thread_event, &joinable_threads_mutex);
56095620
} else {
56105621
gint64 start = mono_msec_ticks ();
56115622
gint64 elapsed = 0;
56125623
while (UnlockedRead (&pending_joinable_thread_count) > 0 && elapsed < timeout) {
5613-
mono_os_cond_timedwait (&zero_pending_joinable_thread_event, &joinable_threads_mutex, timeout - (uint32_t)elapsed);
5624+
mono_coop_cond_timedwait (&zero_pending_joinable_thread_event, &joinable_threads_mutex, timeout - (uint32_t)elapsed);
56145625
elapsed = mono_msec_ticks () - start;
56155626
}
56165627
}
@@ -5658,6 +5669,39 @@ mono_threads_add_joinable_runtime_thread (MonoThreadInfo *thread_info)
56585669
}
56595670
}
56605671

5672+
static void
5673+
threads_add_pending_native_thread_join_call_nolock (gpointer tid)
5674+
{
5675+
if (!pending_native_thread_join_calls)
5676+
pending_native_thread_join_calls = g_hash_table_new (NULL, NULL);
5677+
5678+
gpointer orig_key;
5679+
gpointer value;
5680+
5681+
if (!g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value))
5682+
g_hash_table_insert (pending_native_thread_join_calls, tid, tid);
5683+
}
5684+
5685+
static void
5686+
threads_remove_pending_native_thread_join_call_nolock (gpointer tid)
5687+
{
5688+
if (pending_native_thread_join_calls)
5689+
g_hash_table_remove (pending_native_thread_join_calls, tid);
5690+
5691+
mono_coop_cond_broadcast (&pending_native_thread_join_calls_event);
5692+
}
5693+
5694+
static void
5695+
threads_wait_pending_native_thread_join_call_nolock (gpointer tid)
5696+
{
5697+
gpointer orig_key;
5698+
gpointer value;
5699+
5700+
while (g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value)) {
5701+
mono_coop_cond_wait (&pending_native_thread_join_calls_event, &joinable_threads_mutex);
5702+
}
5703+
}
5704+
56615705
/*
56625706
* mono_add_joinable_thread:
56635707
*
@@ -5689,23 +5733,30 @@ void
56895733
mono_threads_join_threads (void)
56905734
{
56915735
GHashTableIter iter;
5692-
gpointer key;
5693-
gpointer value;
5694-
gboolean found;
5736+
gpointer key = NULL;
5737+
gpointer value = NULL;
5738+
gboolean found = FALSE;
56955739

56965740
/* Fastpath */
56975741
if (!UnlockedRead (&joinable_thread_count))
56985742
return;
56995743

57005744
while (TRUE) {
57015745
joinable_threads_lock ();
5746+
if (found) {
5747+
// Previous native thread join call completed.
5748+
threads_remove_pending_native_thread_join_call_nolock (key);
5749+
}
57025750
found = FALSE;
57035751
if (g_hash_table_size (joinable_threads)) {
57045752
g_hash_table_iter_init (&iter, joinable_threads);
57055753
g_hash_table_iter_next (&iter, &key, (void**)&value);
57065754
g_hash_table_remove (joinable_threads, key);
57075755
UnlockedDecrement (&joinable_thread_count);
57085756
found = TRUE;
5757+
5758+
// Add to table of tid's with pending native thread join call.
5759+
threads_add_pending_native_thread_join_call_nolock (key);
57095760
}
57105761
joinable_threads_unlock ();
57115762
if (found)
@@ -5736,13 +5787,27 @@ mono_thread_join (gpointer tid)
57365787
g_hash_table_remove (joinable_threads, tid);
57375788
UnlockedDecrement (&joinable_thread_count);
57385789
found = TRUE;
5790+
5791+
// Add to table of tid's with pending native join call.
5792+
threads_add_pending_native_thread_join_call_nolock (tid);
5793+
}
5794+
5795+
if (!found) {
5796+
// Wait for any pending native thread join call not yet completed for this tid.
5797+
threads_wait_pending_native_thread_join_call_nolock (tid);
57395798
}
5799+
57405800
joinable_threads_unlock ();
57415801

57425802
if (!found)
57435803
return;
57445804

57455805
threads_native_thread_join_nolock (tid, value);
5806+
5807+
joinable_threads_lock ();
5808+
// Native thread join call completed for this tid.
5809+
threads_remove_pending_native_thread_join_call_nolock (tid);
5810+
joinable_threads_unlock ();
57465811
}
57475812

57485813
void

0 commit comments

Comments
 (0)