@@ -160,6 +160,14 @@ static MonoGHashTable *threads_starting_up = NULL;
160
160
static GHashTable * joinable_threads ;
161
161
static gint32 joinable_thread_count ;
162
162
163
+ static GHashTable * pending_joinable_threads ;
164
+ static gint32 pending_joinable_thread_count ;
165
+
166
+ static mono_cond_t zero_pending_joinable_thread_event ;
167
+
168
+ static void threads_add_pending_joinable_runtime_thread (MonoThreadInfo * mono_thread_info );
169
+ static gboolean threads_wait_pending_joinable_threads (uint32_t timeout );
170
+
163
171
#define SET_CURRENT_OBJECT (x ) mono_tls_set_thread (x)
164
172
#define GET_CURRENT_OBJECT () (MonoInternalThread*) mono_tls_get_thread ()
165
173
@@ -763,6 +771,17 @@ mono_thread_detach_internal (MonoInternalThread *thread)
763
771
764
772
MONO_PROFILER_RAISE (thread_stopping , (thread -> tid ));
765
773
774
+ /*
775
+ * Prevent race condition between thread shutdown and runtime shutdown.
776
+ * Including all runtime threads in the pending joinable count will make
777
+ * sure shutdown will wait for it to get onto the joinable thread list before
778
+ * critical resources have been cleanup (like GC memory). Threads getting onto
779
+ * the joinable thread list should just about to exit and not blocking a potential
780
+ * join call. Owner of threads attached to the runtime but not identified as runtime
781
+ * threads needs to make sure thread detach calls won't race with runtime shutdown.
782
+ */
783
+ threads_add_pending_joinable_runtime_thread (info );
784
+
766
785
#ifndef HOST_WIN32
767
786
mono_w32mutex_abandon ();
768
787
#endif
@@ -775,17 +794,6 @@ mono_thread_detach_internal (MonoInternalThread *thread)
775
794
thread -> abort_exc = NULL ;
776
795
thread -> current_appcontext = NULL ;
777
796
778
- /*
779
- * Prevent race condition between execution of this method and runtime shutdown.
780
- * Adding runtime thread to the joinable threads list will make sure runtime shutdown
781
- * won't complete until added runtime thread have exited. Owner of threads attached to the
782
- * runtime but not identified as runtime threads needs to make sure thread detach calls won't
783
- * race with runtime shutdown.
784
- */
785
- #ifdef HOST_WIN32
786
- mono_threads_add_joinable_runtime_thread (info );
787
- #endif
788
-
789
797
/*
790
798
* thread->synch_cs can be NULL if this was called after
791
799
* ves_icall_System_Threading_InternalThread_Thread_free_internal.
@@ -3031,6 +3039,8 @@ void mono_thread_init (MonoThreadStartCB start_cb,
3031
3039
3032
3040
mono_os_event_init (& background_change_event , FALSE);
3033
3041
3042
+ mono_os_cond_init (& zero_pending_joinable_thread_event );
3043
+
3034
3044
mono_init_static_data_info (& thread_static_info );
3035
3045
mono_init_static_data_info (& context_static_info );
3036
3046
@@ -3122,6 +3132,13 @@ mono_thread_callbacks_init (void)
3122
3132
void
3123
3133
mono_thread_cleanup (void )
3124
3134
{
3135
+ /* Wait for pending threads to park on joinable threads list */
3136
+ /* NOTE, waiting on this should be extremely rare and will only happen */
3137
+ /* under certain specific conditions. */
3138
+ gboolean wait_result = threads_wait_pending_joinable_threads (2000 );
3139
+ if (!wait_result )
3140
+ g_warning ("Waiting on threads to park on joinable thread list timed out." );
3141
+
3125
3142
mono_threads_join_threads ();
3126
3143
3127
3144
#if !defined(RUN_IN_SUBTHREAD ) && !defined(HOST_WIN32 )
@@ -3143,6 +3160,7 @@ mono_thread_cleanup (void)
3143
3160
mono_os_mutex_destroy (& interlocked_mutex );
3144
3161
mono_os_mutex_destroy (& delayed_free_table_mutex );
3145
3162
mono_os_mutex_destroy (& small_id_mutex );
3163
+ mono_os_cond_destroy (& zero_pending_joinable_runtime_thread_event );
3146
3164
mono_os_event_destroy (& background_change_event );
3147
3165
#endif
3148
3166
}
@@ -3367,6 +3385,7 @@ mono_thread_manage (void)
3367
3385
mono_threads_unlock ();
3368
3386
return ;
3369
3387
}
3388
+
3370
3389
mono_threads_unlock ();
3371
3390
3372
3391
do {
@@ -5221,15 +5240,105 @@ threads_add_joinable_thread_nolock (gpointer tid)
5221
5240
}
5222
5241
#endif
5223
5242
5243
+ static void
5244
+ threads_add_pending_joinable_thread (gpointer tid )
5245
+ {
5246
+ joinable_threads_lock ();
5247
+
5248
+ if (!pending_joinable_threads )
5249
+ pending_joinable_threads = g_hash_table_new (NULL , NULL );
5250
+
5251
+ gpointer orig_key ;
5252
+ gpointer value ;
5253
+
5254
+ if (!g_hash_table_lookup_extended (pending_joinable_threads , tid , & orig_key , & value )) {
5255
+ g_hash_table_insert (pending_joinable_threads , tid , tid );
5256
+ UnlockedIncrement (& pending_joinable_thread_count );
5257
+ }
5258
+
5259
+ joinable_threads_unlock ();
5260
+ }
5261
+
5262
+ static void
5263
+ threads_add_pending_joinable_runtime_thread (MonoThreadInfo * mono_thread_info )
5264
+ {
5265
+ g_assert (mono_thread_info );
5266
+
5267
+ if (mono_thread_info -> runtime_thread ) {
5268
+ threads_add_pending_joinable_thread ((gpointer )(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info ))));
5269
+ }
5270
+ }
5271
+
5272
+ static void
5273
+ threads_remove_pending_joinable_thread_nolock (gpointer tid )
5274
+ {
5275
+ gpointer orig_key ;
5276
+ gpointer value ;
5277
+
5278
+ if (pending_joinable_threads && g_hash_table_lookup_extended (pending_joinable_threads , tid , & orig_key , & value )) {
5279
+ g_hash_table_remove (pending_joinable_threads , tid );
5280
+ if (UnlockedDecrement (& pending_joinable_thread_count ) == 0 )
5281
+ mono_os_cond_broadcast (& zero_pending_joinable_thread_event );
5282
+ }
5283
+ }
5284
+
5285
+ static gboolean
5286
+ threads_wait_pending_joinable_threads (uint32_t timeout )
5287
+ {
5288
+ if (UnlockedRead (& pending_joinable_thread_count ) > 0 ) {
5289
+ joinable_threads_lock ();
5290
+ if (timeout == MONO_INFINITE_WAIT ) {
5291
+ while (UnlockedRead (& pending_joinable_thread_count ) > 0 )
5292
+ mono_os_cond_wait (& zero_pending_joinable_thread_event , & joinable_threads_mutex );
5293
+ } else {
5294
+ gint64 start = mono_msec_ticks ();
5295
+ gint64 elapsed = 0 ;
5296
+ while (UnlockedRead (& pending_joinable_thread_count ) > 0 && elapsed < timeout ) {
5297
+ mono_os_cond_timedwait (& zero_pending_joinable_thread_event , & joinable_threads_mutex , timeout - (uint32_t )elapsed );
5298
+ elapsed = mono_msec_ticks () - start ;
5299
+ }
5300
+ }
5301
+ joinable_threads_unlock ();
5302
+ }
5303
+
5304
+ return UnlockedRead (& pending_joinable_thread_count ) == 0 ;
5305
+ }
5306
+
5307
+ static void
5308
+ threads_add_unique_joinable_thread_nolock (gpointer tid )
5309
+ {
5310
+ if (!joinable_threads )
5311
+ joinable_threads = g_hash_table_new (NULL , NULL );
5312
+
5313
+ gpointer orig_key ;
5314
+ gpointer value ;
5315
+
5316
+ if (!g_hash_table_lookup_extended (joinable_threads , tid , & orig_key , & value )) {
5317
+ threads_add_joinable_thread_nolock (tid );
5318
+ UnlockedIncrement (& joinable_thread_count );
5319
+ }
5320
+ }
5321
+
5224
5322
void
5225
5323
mono_threads_add_joinable_runtime_thread (gpointer thread_info )
5226
5324
{
5227
5325
g_assert (thread_info );
5228
5326
MonoThreadInfo * mono_thread_info = (MonoThreadInfo * )thread_info ;
5229
5327
5230
5328
if (mono_thread_info -> runtime_thread ) {
5231
- if (mono_atomic_cas_i32 (& mono_thread_info -> thread_pending_native_join , TRUE, FALSE) == FALSE)
5232
- mono_threads_add_joinable_thread ((gpointer )(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info ))));
5329
+ gpointer tid = (gpointer )(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info )));
5330
+
5331
+ joinable_threads_lock ();
5332
+
5333
+ // Add to joinable thread list, if not already included.
5334
+ threads_add_unique_joinable_thread_nolock (tid );
5335
+
5336
+ // Remove thread from pending joinable list, if present.
5337
+ threads_remove_pending_joinable_thread_nolock (tid );
5338
+
5339
+ joinable_threads_unlock ();
5340
+
5341
+ mono_gc_finalize_notify ();
5233
5342
}
5234
5343
}
5235
5344
@@ -5248,15 +5357,7 @@ mono_threads_add_joinable_thread (gpointer tid)
5248
5357
* we have time (in the finalizer thread).
5249
5358
*/
5250
5359
joinable_threads_lock ();
5251
- if (!joinable_threads )
5252
- joinable_threads = g_hash_table_new (NULL , NULL );
5253
-
5254
- gpointer orig_key ;
5255
- gpointer value ;
5256
- if (!g_hash_table_lookup_extended (joinable_threads , tid , & orig_key , & value )) {
5257
- threads_add_joinable_thread_nolock (tid );
5258
- UnlockedIncrement (& joinable_thread_count );
5259
- }
5360
+ threads_add_unique_joinable_thread_nolock (tid );
5260
5361
joinable_threads_unlock ();
5261
5362
5262
5363
mono_gc_finalize_notify ();
0 commit comments