[threadpool] Let the runtime abort and wait for threads on shutdown (#4348)
[mono.git] / mono / metadata / threadpool.c
1 /*
2  * threadpool.c: Microsoft threadpool runtime support
3  *
4  * Author:
5  *      Ludovic Henry (ludovic.henry@xamarin.com)
6  *
7  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10
11 //
12 // Copyright (c) Microsoft. All rights reserved.
13 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 //
15 // Files:
16 //  - src/vm/comthreadpool.cpp
17 //  - src/vm/win32threadpoolcpp
18 //  - src/vm/threadpoolrequest.cpp
19 //  - src/vm/hillclimbing.cpp
20 //
21 // Ported from C++ to C and adjusted to Mono runtime
22
23 #include <stdlib.h>
24 #define _USE_MATH_DEFINES // needed by MSVC to define math constants
25 #include <math.h>
26 #include <config.h>
27 #include <glib.h>
28
29 #include <mono/metadata/class-internals.h>
30 #include <mono/metadata/exception.h>
31 #include <mono/metadata/gc-internals.h>
32 #include <mono/metadata/object.h>
33 #include <mono/metadata/object-internals.h>
34 #include <mono/metadata/threadpool.h>
35 #include <mono/metadata/threadpool-worker.h>
36 #include <mono/metadata/threadpool-io.h>
37 #include <mono/metadata/w32event.h>
38 #include <mono/utils/atomic.h>
39 #include <mono/utils/mono-compiler.h>
40 #include <mono/utils/mono-complex.h>
41 #include <mono/utils/mono-lazy-init.h>
42 #include <mono/utils/mono-logger.h>
43 #include <mono/utils/mono-logger-internals.h>
44 #include <mono/utils/mono-proclib.h>
45 #include <mono/utils/mono-threads.h>
46 #include <mono/utils/mono-time.h>
47 #include <mono/utils/refcount.h>
48
49 typedef struct {
50         MonoDomain *domain;
51         /* Number of outstanding jobs */
52         gint32 outstanding_request;
53         /* Number of currently executing jobs */
54         gint32 threadpool_jobs;
55         /* Signalled when threadpool_jobs + outstanding_request is 0 */
56         /* Protected by threadpool.domains_lock */
57         MonoCoopCond cleanup_cond;
58 } ThreadPoolDomain;
59
60 typedef union {
61         struct {
62                 gint16 starting; /* starting, but not yet in worker_callback */
63                 gint16 working; /* executing worker_callback */
64         } _;
65         gint32 as_gint32;
66 } ThreadPoolCounter;
67
68 typedef struct {
69         MonoRefCount ref;
70
71         GPtrArray *domains; // ThreadPoolDomain* []
72         MonoCoopMutex domains_lock;
73
74         ThreadPoolCounter counters;
75
76         gint32 limit_io_min;
77         gint32 limit_io_max;
78 } ThreadPool;
79
80 static mono_lazy_init_t status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
81
82 static ThreadPool threadpool;
83
84 #define COUNTER_ATOMIC(var,block) \
85         do { \
86                 ThreadPoolCounter __old; \
87                 do { \
88                         (var) = __old = COUNTER_READ (); \
89                         { block; } \
90                         if (!(counter._.starting >= 0)) \
91                                 g_error ("%s: counter._.starting = %d, but should be >= 0", __func__, counter._.starting); \
92                         if (!(counter._.working >= 0)) \
93                                 g_error ("%s: counter._.working = %d, but should be >= 0", __func__, counter._.working); \
94                 } while (InterlockedCompareExchange (&threadpool.counters.as_gint32, (var).as_gint32, __old.as_gint32) != __old.as_gint32); \
95         } while (0)
96
97 static inline ThreadPoolCounter
98 COUNTER_READ (void)
99 {
100         ThreadPoolCounter counter;
101         counter.as_gint32 = InterlockedRead (&threadpool.counters.as_gint32);
102         return counter;
103 }
104
105 static inline void
106 domains_lock (void)
107 {
108         mono_coop_mutex_lock (&threadpool.domains_lock);
109 }
110
111 static inline void
112 domains_unlock (void)
113 {
114         mono_coop_mutex_unlock (&threadpool.domains_lock);
115 }
116
117 static void
118 destroy (gpointer unused)
119 {
120         g_ptr_array_free (threadpool.domains, TRUE);
121         mono_coop_mutex_destroy (&threadpool.domains_lock);
122 }
123
124 static void
125 initialize (void)
126 {
127         g_assert (sizeof (ThreadPoolCounter) == sizeof (gint32));
128
129         mono_refcount_init (&threadpool, destroy);
130
131         threadpool.domains = g_ptr_array_new ();
132         mono_coop_mutex_init (&threadpool.domains_lock);
133
134         threadpool.limit_io_min = mono_cpu_count ();
135         threadpool.limit_io_max = CLAMP (threadpool.limit_io_min * 100, MIN (threadpool.limit_io_min, 200), MAX (threadpool.limit_io_min, 200));
136
137         mono_threadpool_worker_init ();
138 }
139
140 static void
141 cleanup (void)
142 {
143         mono_threadpool_worker_cleanup ();
144
145         mono_refcount_dec (&threadpool);
146 }
147
148 gboolean
149 mono_threadpool_enqueue_work_item (MonoDomain *domain, MonoObject *work_item, MonoError *error)
150 {
151         static MonoClass *threadpool_class = NULL;
152         static MonoMethod *unsafe_queue_custom_work_item_method = NULL;
153         MonoDomain *current_domain;
154         MonoBoolean f;
155         gpointer args [2];
156
157         mono_error_init (error);
158         g_assert (work_item);
159
160         if (!threadpool_class)
161                 threadpool_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading", "ThreadPool");
162
163         if (!unsafe_queue_custom_work_item_method)
164                 unsafe_queue_custom_work_item_method = mono_class_get_method_from_name (threadpool_class, "UnsafeQueueCustomWorkItem", 2);
165         g_assert (unsafe_queue_custom_work_item_method);
166
167         f = FALSE;
168
169         args [0] = (gpointer) work_item;
170         args [1] = (gpointer) &f;
171
172         current_domain = mono_domain_get ();
173         if (current_domain == domain) {
174                 mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, error);
175                 return_val_if_nok (error, FALSE);
176         } else {
177                 mono_thread_push_appdomain_ref (domain);
178                 if (mono_domain_set (domain, FALSE)) {
179                         mono_runtime_invoke_checked (unsafe_queue_custom_work_item_method, NULL, args, error);
180                         if (!is_ok (error)) {
181                                 mono_thread_pop_appdomain_ref ();
182                                 return FALSE;
183                         }
184                         mono_domain_set (current_domain, TRUE);
185                 }
186                 mono_thread_pop_appdomain_ref ();
187         }
188         return TRUE;
189 }
190
191 /* LOCKING: domains_lock must be held. */
192 static ThreadPoolDomain *
193 tpdomain_create (MonoDomain *domain)
194 {
195         ThreadPoolDomain *tpdomain;
196
197         tpdomain = g_new0 (ThreadPoolDomain, 1);
198         tpdomain->domain = domain;
199         mono_coop_cond_init (&tpdomain->cleanup_cond);
200
201         g_ptr_array_add (threadpool.domains, tpdomain);
202
203         return tpdomain;
204 }
205
206 /* LOCKING: domains_lock must be held. */
207 static gboolean
208 tpdomain_remove (ThreadPoolDomain *tpdomain)
209 {
210         g_assert (tpdomain);
211         return g_ptr_array_remove (threadpool.domains, tpdomain);
212 }
213
214 /* LOCKING: domains_lock must be held */
215 static ThreadPoolDomain *
216 tpdomain_get (MonoDomain *domain)
217 {
218         gint i;
219
220         g_assert (domain);
221
222         for (i = 0; i < threadpool.domains->len; ++i) {
223                 ThreadPoolDomain *tpdomain;
224
225                 tpdomain = (ThreadPoolDomain *)g_ptr_array_index (threadpool.domains, i);
226                 if (tpdomain->domain == domain)
227                         return tpdomain;
228         }
229
230         return NULL;
231 }
232
233 static void
234 tpdomain_free (ThreadPoolDomain *tpdomain)
235 {
236         g_free (tpdomain);
237 }
238
239 /* LOCKING: domains_lock must be held */
240 static ThreadPoolDomain *
241 tpdomain_get_next (ThreadPoolDomain *current)
242 {
243         ThreadPoolDomain *tpdomain = NULL;
244         gint len;
245
246         len = threadpool.domains->len;
247         if (len > 0) {
248                 gint i, current_idx = -1;
249                 if (current) {
250                         for (i = 0; i < len; ++i) {
251                                 if (current == g_ptr_array_index (threadpool.domains, i)) {
252                                         current_idx = i;
253                                         break;
254                                 }
255                         }
256                 }
257                 for (i = current_idx + 1; i < len + current_idx + 1; ++i) {
258                         ThreadPoolDomain *tmp = (ThreadPoolDomain *)g_ptr_array_index (threadpool.domains, i % len);
259                         if (tmp->outstanding_request > 0) {
260                                 tpdomain = tmp;
261                                 break;
262                         }
263                 }
264         }
265
266         return tpdomain;
267 }
268
269 static MonoObject*
270 try_invoke_perform_wait_callback (MonoObject** exc, MonoError *error)
271 {
272         HANDLE_FUNCTION_ENTER ();
273         mono_error_init (error);
274         MonoObject *res = mono_runtime_try_invoke (mono_defaults.threadpool_perform_wait_callback_method, NULL, NULL, exc, error);
275         HANDLE_FUNCTION_RETURN_VAL (res);
276 }
277
278 static void
279 worker_callback (gpointer unused)
280 {
281         MonoError error;
282         ThreadPoolDomain *tpdomain, *previous_tpdomain;
283         ThreadPoolCounter counter;
284         MonoInternalThread *thread;
285
286         if (!mono_refcount_tryinc (&threadpool))
287                 return;
288
289         thread = mono_thread_internal_current ();
290
291         COUNTER_ATOMIC (counter, {
292                 if (!(counter._.working < 32767 /* G_MAXINT16 */))
293                         g_error ("%s: counter._.working = %d, but should be < 32767", __func__, counter._.working);
294
295                 counter._.starting --;
296                 counter._.working ++;
297         });
298
299         if (mono_runtime_is_shutting_down ()) {
300                 COUNTER_ATOMIC (counter, {
301                         counter._.working --;
302                 });
303
304                 mono_refcount_dec (&threadpool);
305                 return;
306         }
307
308         /*
309          * This is needed so there is always an lmf frame in the runtime invoke call below,
310          * so ThreadAbortExceptions are caught even if the thread is in native code.
311          */
312         mono_defaults.threadpool_perform_wait_callback_method->save_lmf = TRUE;
313
314         domains_lock ();
315
316         previous_tpdomain = NULL;
317
318         while (!mono_runtime_is_shutting_down ()) {
319                 gboolean retire = FALSE;
320
321                 if (thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) {
322                         domains_unlock ();
323                         if (mono_thread_interruption_checkpoint ()) {
324                                 domains_lock ();
325                                 continue;
326                         }
327                         domains_lock ();
328                 }
329
330                 tpdomain = tpdomain_get_next (previous_tpdomain);
331                 if (!tpdomain)
332                         break;
333
334                 tpdomain->outstanding_request --;
335                 g_assert (tpdomain->outstanding_request >= 0);
336
337                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] worker running in domain %p (outstanding requests %d)",
338                         mono_native_thread_id_get (), tpdomain->domain, tpdomain->outstanding_request);
339
340                 g_assert (tpdomain->threadpool_jobs >= 0);
341                 tpdomain->threadpool_jobs ++;
342
343                 domains_unlock ();
344
345                 mono_thread_set_name_internal (thread, mono_string_new (mono_get_root_domain (), "Threadpool worker"), FALSE, TRUE, &error);
346                 mono_error_assert_ok (&error);
347
348                 mono_thread_clr_state (thread, (MonoThreadState)~ThreadState_Background);
349                 if (!mono_thread_test_state (thread , ThreadState_Background))
350                         ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background);
351
352                 mono_thread_push_appdomain_ref (tpdomain->domain);
353                 if (mono_domain_set (tpdomain->domain, FALSE)) {
354                         MonoObject *exc = NULL, *res;
355
356                         res = try_invoke_perform_wait_callback (&exc, &error);
357                         if (exc || !mono_error_ok(&error)) {
358                                 if (exc == NULL)
359                                         exc = (MonoObject *) mono_error_convert_to_exception (&error);
360                                 else
361                                         mono_error_cleanup (&error);
362                                 mono_thread_internal_unhandled_exception (exc);
363                         } else if (res && *(MonoBoolean*) mono_object_unbox (res) == FALSE) {
364                                 retire = TRUE;
365                         }
366
367                         mono_domain_set (mono_get_root_domain (), TRUE);
368                 }
369                 mono_thread_pop_appdomain_ref ();
370
371                 domains_lock ();
372
373                 tpdomain->threadpool_jobs --;
374                 g_assert (tpdomain->threadpool_jobs >= 0);
375
376                 if (tpdomain->outstanding_request + tpdomain->threadpool_jobs == 0 && mono_domain_is_unloading (tpdomain->domain)) {
377                         gboolean removed;
378
379                         removed = tpdomain_remove (tpdomain);
380                         g_assert (removed);
381
382                         mono_coop_cond_signal (&tpdomain->cleanup_cond);
383                         tpdomain = NULL;
384                 }
385
386                 if (retire)
387                         break;
388
389                 previous_tpdomain = tpdomain;
390         }
391
392         domains_unlock ();
393
394         COUNTER_ATOMIC (counter, {
395                 counter._.working --;
396         });
397
398         mono_refcount_dec (&threadpool);
399 }
400
401 void
402 mono_threadpool_cleanup (void)
403 {
404 #ifndef DISABLE_SOCKETS
405         mono_threadpool_io_cleanup ();
406 #endif
407         mono_lazy_cleanup (&status, cleanup);
408 }
409
410 MonoAsyncResult *
411 mono_threadpool_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod *method, gpointer *params, MonoError *error)
412 {
413         static MonoClass *async_call_klass = NULL;
414         MonoMethodMessage *message;
415         MonoAsyncResult *async_result;
416         MonoAsyncCall *async_call;
417         MonoDelegate *async_callback = NULL;
418         MonoObject *state = NULL;
419
420         if (!async_call_klass)
421                 async_call_klass = mono_class_load_from_name (mono_defaults.corlib, "System", "MonoAsyncCall");
422
423         mono_error_init (error);
424
425         message = mono_method_call_message_new (method, params, mono_get_delegate_invoke (method->klass), (params != NULL) ? (&async_callback) : NULL, (params != NULL) ? (&state) : NULL, error);
426         return_val_if_nok (error, NULL);
427
428         async_call = (MonoAsyncCall*) mono_object_new_checked (domain, async_call_klass, error);
429         return_val_if_nok (error, NULL);
430
431         MONO_OBJECT_SETREF (async_call, msg, message);
432         MONO_OBJECT_SETREF (async_call, state, state);
433
434         if (async_callback) {
435                 MONO_OBJECT_SETREF (async_call, cb_method, mono_get_delegate_invoke (((MonoObject*) async_callback)->vtable->klass));
436                 MONO_OBJECT_SETREF (async_call, cb_target, async_callback);
437         }
438
439         async_result = mono_async_result_new (domain, NULL, async_call->state, NULL, (MonoObject*) async_call, error);
440         return_val_if_nok (error, NULL);
441         MONO_OBJECT_SETREF (async_result, async_delegate, target);
442
443         mono_threadpool_enqueue_work_item (domain, (MonoObject*) async_result, error);
444         return_val_if_nok (error, NULL);
445
446         return async_result;
447 }
448
449 MonoObject *
450 mono_threadpool_end_invoke (MonoAsyncResult *ares, MonoArray **out_args, MonoObject **exc, MonoError *error)
451 {
452         MonoAsyncCall *ac;
453
454         mono_error_init (error);
455         g_assert (exc);
456         g_assert (out_args);
457
458         *exc = NULL;
459         *out_args = NULL;
460
461         /* check if already finished */
462         mono_monitor_enter ((MonoObject*) ares);
463
464         if (ares->endinvoke_called) {
465                 mono_error_set_invalid_operation(error, "Delegate EndInvoke method called more than once");
466                 mono_monitor_exit ((MonoObject*) ares);
467                 return NULL;
468         }
469
470         ares->endinvoke_called = 1;
471
472         /* wait until we are really finished */
473         if (ares->completed) {
474                 mono_monitor_exit ((MonoObject *) ares);
475         } else {
476                 gpointer wait_event;
477                 if (ares->handle) {
478                         wait_event = mono_wait_handle_get_handle ((MonoWaitHandle*) ares->handle);
479                 } else {
480                         wait_event = mono_w32event_create (TRUE, FALSE);
481                         g_assert(wait_event);
482                         MonoWaitHandle *wait_handle = mono_wait_handle_new (mono_object_domain (ares), wait_event, error);
483                         if (!is_ok (error)) {
484                                 mono_w32event_close (wait_event);
485                                 return NULL;
486                         }
487                         MONO_OBJECT_SETREF (ares, handle, (MonoObject*) wait_handle);
488                 }
489                 mono_monitor_exit ((MonoObject*) ares);
490                 MONO_ENTER_GC_SAFE;
491 #ifdef HOST_WIN32
492                 WaitForSingleObjectEx (wait_event, INFINITE, TRUE);
493 #else
494                 mono_w32handle_wait_one (wait_event, MONO_INFINITE_WAIT, TRUE);
495 #endif
496                 MONO_EXIT_GC_SAFE;
497         }
498
499         ac = (MonoAsyncCall*) ares->object_data;
500         g_assert (ac);
501
502         *exc = ac->msg->exc; /* FIXME: GC add write barrier */
503         *out_args = ac->out_args;
504         return ac->res;
505 }
506
507 gboolean
508 mono_threadpool_remove_domain_jobs (MonoDomain *domain, int timeout)
509 {
510         gint64 end;
511         ThreadPoolDomain *tpdomain;
512         gboolean ret;
513
514         g_assert (domain);
515         g_assert (timeout >= -1);
516
517         g_assert (mono_domain_is_unloading (domain));
518
519         if (timeout != -1)
520                 end = mono_msec_ticks () + timeout;
521
522 #ifndef DISABLE_SOCKETS
523         mono_threadpool_io_remove_domain_jobs (domain);
524         if (timeout != -1) {
525                 if (mono_msec_ticks () > end)
526                         return FALSE;
527         }
528 #endif
529
530         /*
531          * Wait for all threads which execute jobs in the domain to exit.
532          * The is_unloading () check in worker_request () ensures that
533          * no new jobs are added after we enter the lock below.
534          */
535
536         if (!mono_lazy_is_initialized (&status))
537                 return TRUE;
538
539         mono_refcount_inc (&threadpool);
540
541         domains_lock ();
542
543         tpdomain = tpdomain_get (domain);
544         if (!tpdomain) {
545                 domains_unlock ();
546                 mono_refcount_dec (&threadpool);
547                 return TRUE;
548         }
549
550         ret = TRUE;
551
552         while (tpdomain->outstanding_request + tpdomain->threadpool_jobs > 0) {
553                 if (timeout == -1) {
554                         mono_coop_cond_wait (&tpdomain->cleanup_cond, &threadpool.domains_lock);
555                 } else {
556                         gint64 now;
557                         gint res;
558
559                         now = mono_msec_ticks();
560                         if (now > end) {
561                                 ret = FALSE;
562                                 break;
563                         }
564
565                         res = mono_coop_cond_timedwait (&tpdomain->cleanup_cond, &threadpool.domains_lock, end - now);
566                         if (res != 0) {
567                                 ret = FALSE;
568                                 break;
569                         }
570                 }
571         }
572
573         /* Remove from the list the worker threads look at */
574         tpdomain_remove (tpdomain);
575
576         domains_unlock ();
577
578         mono_coop_cond_destroy (&tpdomain->cleanup_cond);
579         tpdomain_free (tpdomain);
580
581         mono_refcount_dec (&threadpool);
582
583         return ret;
584 }
585
586 void
587 mono_threadpool_suspend (void)
588 {
589         if (mono_lazy_is_initialized (&status))
590                 mono_threadpool_worker_set_suspended (TRUE);
591 }
592
593 void
594 mono_threadpool_resume (void)
595 {
596         if (mono_lazy_is_initialized (&status))
597                 mono_threadpool_worker_set_suspended (FALSE);
598 }
599
600 void
601 ves_icall_System_Threading_ThreadPool_GetAvailableThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads)
602 {
603         ThreadPoolCounter counter;
604
605         if (!worker_threads || !completion_port_threads)
606                 return;
607
608         if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) {
609                 *worker_threads = 0;
610                 *completion_port_threads = 0;
611                 return;
612         }
613
614         counter = COUNTER_READ ();
615
616         *worker_threads = MAX (0, mono_threadpool_worker_get_max () - counter._.working);
617         *completion_port_threads = threadpool.limit_io_max;
618
619         mono_refcount_dec (&threadpool);
620 }
621
622 void
623 ves_icall_System_Threading_ThreadPool_GetMinThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads)
624 {
625         if (!worker_threads || !completion_port_threads)
626                 return;
627
628         if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) {
629                 *worker_threads = 0;
630                 *completion_port_threads = 0;
631                 return;
632         }
633
634         *worker_threads = mono_threadpool_worker_get_min ();
635         *completion_port_threads = threadpool.limit_io_min;
636
637         mono_refcount_dec (&threadpool);
638 }
639
640 void
641 ves_icall_System_Threading_ThreadPool_GetMaxThreadsNative (gint32 *worker_threads, gint32 *completion_port_threads)
642 {
643         if (!worker_threads || !completion_port_threads)
644                 return;
645
646         if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) {
647                 *worker_threads = 0;
648                 *completion_port_threads = 0;
649                 return;
650         }
651
652         *worker_threads = mono_threadpool_worker_get_max ();
653         *completion_port_threads = threadpool.limit_io_max;
654
655         mono_refcount_dec (&threadpool);
656 }
657
658 MonoBoolean
659 ves_icall_System_Threading_ThreadPool_SetMinThreadsNative (gint32 worker_threads, gint32 completion_port_threads)
660 {
661         if (completion_port_threads <= 0 || completion_port_threads > threadpool.limit_io_max)
662                 return FALSE;
663
664         if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool))
665                 return FALSE;
666
667         if (!mono_threadpool_worker_set_min (worker_threads)) {
668                 mono_refcount_dec (&threadpool);
669                 return FALSE;
670         }
671
672         threadpool.limit_io_min = completion_port_threads;
673
674         mono_refcount_dec (&threadpool);
675         return TRUE;
676 }
677
678 MonoBoolean
679 ves_icall_System_Threading_ThreadPool_SetMaxThreadsNative (gint32 worker_threads, gint32 completion_port_threads)
680 {
681         gint cpu_count = mono_cpu_count ();
682
683         if (completion_port_threads < threadpool.limit_io_min || completion_port_threads < cpu_count)
684                 return FALSE;
685
686         if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool))
687                 return FALSE;
688
689         if (!mono_threadpool_worker_set_max (worker_threads)) {
690                 mono_refcount_dec (&threadpool);
691                 return FALSE;
692         }
693
694         threadpool.limit_io_max = completion_port_threads;
695
696         mono_refcount_dec (&threadpool);
697         return TRUE;
698 }
699
700 void
701 ves_icall_System_Threading_ThreadPool_InitializeVMTp (MonoBoolean *enable_worker_tracking)
702 {
703         if (enable_worker_tracking) {
704                 // TODO implement some kind of switch to have the possibily to use it
705                 *enable_worker_tracking = FALSE;
706         }
707
708         mono_lazy_initialize (&status, initialize);
709 }
710
711 MonoBoolean
712 ves_icall_System_Threading_ThreadPool_NotifyWorkItemComplete (void)
713 {
714         if (mono_domain_is_unloading (mono_domain_get ()) || mono_runtime_is_shutting_down ())
715                 return FALSE;
716
717         return mono_threadpool_worker_notify_completed ();
718 }
719
720 void
721 ves_icall_System_Threading_ThreadPool_NotifyWorkItemProgressNative (void)
722 {
723         mono_threadpool_worker_notify_completed ();
724 }
725
726 void
727 ves_icall_System_Threading_ThreadPool_ReportThreadStatus (MonoBoolean is_working)
728 {
729         // TODO
730         MonoError error;
731         mono_error_set_not_implemented (&error, "");
732         mono_error_set_pending_exception (&error);
733 }
734
735 MonoBoolean
736 ves_icall_System_Threading_ThreadPool_RequestWorkerThread (void)
737 {
738         MonoDomain *domain;
739         ThreadPoolDomain *tpdomain;
740         ThreadPoolCounter counter;
741
742         domain = mono_domain_get ();
743         if (mono_domain_is_unloading (domain))
744                 return FALSE;
745
746         if (!mono_lazy_initialize (&status, initialize) || !mono_refcount_tryinc (&threadpool)) {
747                 /* threadpool has been destroyed, we are shutting down */
748                 return FALSE;
749         }
750
751         domains_lock ();
752
753         tpdomain = tpdomain_get (domain);
754         if (!tpdomain) {
755                 /* synchronize with mono_threadpool_remove_domain_jobs */
756                 if (mono_domain_is_unloading (domain)) {
757                         domains_unlock ();
758                         mono_refcount_dec (&threadpool);
759                         return FALSE;
760                 }
761
762                 tpdomain = tpdomain_create (domain);
763         }
764
765         g_assert (tpdomain);
766
767         tpdomain->outstanding_request ++;
768         g_assert (tpdomain->outstanding_request >= 1);
769
770         domains_unlock ();
771
772         COUNTER_ATOMIC (counter, {
773                 if (counter._.starting == 16) {
774                         mono_refcount_dec (&threadpool);
775                         return TRUE;
776                 }
777
778                 counter._.starting ++;
779         });
780
781         mono_threadpool_worker_enqueue (worker_callback, NULL);
782
783         mono_refcount_dec (&threadpool);
784         return TRUE;
785 }
786
787 MonoBoolean G_GNUC_UNUSED
788 ves_icall_System_Threading_ThreadPool_PostQueuedCompletionStatus (MonoNativeOverlapped *native_overlapped)
789 {
790         /* This copy the behavior of the current Mono implementation */
791         MonoError error;
792         mono_error_set_not_implemented (&error, "");
793         mono_error_set_pending_exception (&error);
794         return FALSE;
795 }
796
797 MonoBoolean G_GNUC_UNUSED
798 ves_icall_System_Threading_ThreadPool_BindIOCompletionCallbackNative (gpointer file_handle)
799 {
800         /* This copy the behavior of the current Mono implementation */
801         return TRUE;
802 }
803
804 MonoBoolean G_GNUC_UNUSED
805 ves_icall_System_Threading_ThreadPool_IsThreadPoolHosted (void)
806 {
807         return FALSE;
808 }