2 * threads.c: Thread support internal calls
5 * Dick Porter (dick@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Patrik Torstensson (patrik.torstensson@labs2.com)
9 * (C) 2001 Ximian, Inc.
16 #include <mono/metadata/object.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/threadpool.h>
21 #include <mono/metadata/threads-types.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/environment.h>
24 #include <mono/metadata/monitor.h>
25 #include <mono/io-layer/io-layer.h>
27 #include <mono/os/gc_wrapper.h>
30 #undef THREAD_WAIT_DEBUG
34 guint32 (*func)(void *);
46 * The "os_handle" field of the WaitHandle class.
48 static MonoClassField *wait_handle_os_handle_field = NULL;
50 /* Controls access to the 'threads' hash table */
51 static CRITICAL_SECTION threads_mutex;
53 /* The hash of existing threads (key is thread ID) that need joining
56 static MonoGHashTable *threads=NULL;
58 /* The TLS key that holds the MonoObject assigned to each thread */
59 static guint32 current_object_key = -1;
61 /* function called at thread start */
62 static MonoThreadStartCB mono_thread_start_cb = NULL;
64 /* function called at thread attach */
65 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
67 /* function called at thread cleanup */
68 static MonoThreadCleanupFunc mono_thread_cleanup = NULL;
70 /* function called when a new thread has been created */
71 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
73 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
74 static guint32 slothash_key = -1;
76 /* The default stack size for each thread */
77 static guint32 default_stacksize = 0;
79 static void thread_adjust_static_data (MonoThread *thread);
81 /* Spin lock for InterlockedXXX 64 bit functions */
82 static CRITICAL_SECTION interlocked_mutex;
84 /* handle_store() and handle_remove() manage the array of threads that
85 * still need to be waited for when the main thread exits.
87 static void handle_store(MonoThread *thread)
89 EnterCriticalSection(&threads_mutex);
92 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
97 threads=mono_g_hash_table_new(NULL, NULL);
100 /* We don't need to duplicate thread->handle, because it is
101 * only closed when the thread object is finalized by the GC.
103 mono_g_hash_table_insert(threads, GUINT_TO_POINTER(thread->tid), thread);
104 LeaveCriticalSection(&threads_mutex);
107 static void handle_remove(guint32 tid)
110 g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
113 EnterCriticalSection(&threads_mutex);
115 mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid));
117 LeaveCriticalSection(&threads_mutex);
119 /* Don't close the handle here, wait for the object finalizer
120 * to do it. Otherwise, the following race condition applies:
122 * 1) Thread exits (and handle_remove() closes the handle)
124 * 2) Some other handle is reassigned the same slot
126 * 3) Another thread tries to join the first thread, and
127 * blocks waiting for the reassigned handle to be signalled
128 * (which might never happen). This is possible, because the
129 * thread calling Join() still has a reference to the first
134 static void thread_cleanup (MonoThread *thread)
136 mono_monitor_try_enter ((MonoObject *)thread, INFINITE);
137 thread->state |= ThreadState_Stopped;
138 mono_monitor_exit ((MonoObject *)thread);
140 mono_profiler_thread_end (thread->tid);
141 handle_remove (thread->tid);
143 mono_thread_pop_appdomain_ref ();
145 if (mono_thread_cleanup)
146 mono_thread_cleanup (thread);
149 static guint32 start_wrapper(void *data)
151 struct StartInfo *start_info=(struct StartInfo *)data;
152 guint32 (*start_func)(void *);
155 MonoThread *thread=start_info->obj;
158 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
159 GetCurrentThreadId ());
162 /* We can be sure start_info->obj->tid and
163 * start_info->obj->handle have been set, because the thread
164 * was created suspended, and these values were set before the
170 TlsSetValue (current_object_key, thread);
172 if (!mono_domain_set (start_info->domain, FALSE)) {
173 /* No point in raising an appdomain_unloaded exception here */
174 /* FIXME: Cleanup here */
178 start_func = start_info->func;
179 this = start_info->this;
181 /* This MUST be called before any managed code can be
182 * executed, as it calls the callback function that (for the
183 * jit) sets the lmf marker.
185 mono_thread_new_init (tid, &tid, start_func);
186 thread->stack_ptr = &tid;
189 g_message (G_GNUC_PRETTY_FUNCTION
190 ": (%d,%d) Setting thread stack to %p",
191 GetCurrentThreadId (), getpid (), thread->stack_ptr);
195 g_message (G_GNUC_PRETTY_FUNCTION
196 ": (%d) Setting current_object_key to %p",
197 GetCurrentThreadId (), thread);
200 mono_profiler_thread_start (tid);
202 if(thread->start_notify!=NULL) {
203 /* Let the thread that called Start() know we're
206 ReleaseSemaphore (thread->start_notify, 1, NULL);
211 /* Every thread references the appdomain which created it */
212 mono_thread_push_appdomain_ref (mono_domain_get ());
214 thread_adjust_static_data (thread);
218 /* If the thread calls ExitThread at all, this remaining code
219 * will not be executed, but the main thread will eventually
220 * call thread_cleanup() on this thread's behalf.
224 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
225 GetCurrentThreadId ());
228 /* Remove the reference to the thread object in the TLS data,
229 * so the thread object can be finalized. This won't be
230 * reached if the thread threw an uncaught exception, so those
231 * thread handles will stay referenced :-( (This is due to
232 * missing support for scanning thread-specific data in the
233 * Boehm GC - the io-layer keeps a GC-visible hash of pointers
236 TlsSetValue (current_object_key, NULL);
238 thread_cleanup (thread);
243 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
245 if (mono_thread_start_cb) {
246 mono_thread_start_cb (tid, stack_start, func);
249 if (mono_thread_callbacks)
250 (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
253 void mono_threads_set_default_stacksize (guint32 stacksize)
255 default_stacksize = stacksize;
258 guint32 mono_threads_get_default_stacksize (void)
260 return default_stacksize;
263 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
266 HANDLE thread_handle;
267 struct StartInfo *start_info;
270 thread=(MonoThread *)mono_object_new (domain,
271 mono_defaults.thread_class);
273 start_info=g_new0 (struct StartInfo, 1);
274 start_info->func = func;
275 start_info->obj = thread;
276 start_info->domain = domain;
277 start_info->this = arg;
279 /* Create suspended, so we can do some housekeeping before the thread
282 thread_handle = CreateThread(NULL, default_stacksize, start_wrapper, start_info,
283 CREATE_SUSPENDED, &tid);
285 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
288 g_assert (thread_handle);
290 thread->handle=thread_handle;
293 handle_store(thread);
295 ResumeThread (thread_handle);
299 mono_thread_attach (MonoDomain *domain)
302 HANDLE thread_handle;
305 if ((thread = mono_thread_current ())) {
306 g_warning ("mono_thread_attach called for an already attached thread");
307 if (mono_thread_attach_cb) {
308 mono_thread_attach_cb (tid, &tid);
313 thread = (MonoThread *)mono_object_new (domain,
314 mono_defaults.thread_class);
316 thread_handle = GetCurrentThread ();
317 g_assert (thread_handle);
319 tid=GetCurrentThreadId ();
321 thread->handle=thread_handle;
325 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
329 handle_store(thread);
332 g_message (G_GNUC_PRETTY_FUNCTION
333 ": (%d) Setting current_object_key to %p",
334 GetCurrentThreadId (), thread);
337 TlsSetValue (current_object_key, thread);
338 mono_domain_set (domain, TRUE);
340 thread_adjust_static_data (thread);
342 if (mono_thread_attach_cb) {
343 mono_thread_attach_cb (tid, &tid);
350 ves_icall_System_Threading_ThreadPool_GetAvailableThreads (int *workerThreads, int *completionPortThreads)
356 busy = (int) InterlockedCompareExchange (&busy_worker_threads, 0, -1);
357 *workerThreads = mono_max_worker_threads - busy;
358 *completionPortThreads = 0;
362 ves_icall_System_Threading_ThreadPool_GetMaxThreads (int *workerThreads, int *completionPortThreads)
367 *workerThreads = mono_max_worker_threads;
368 *completionPortThreads = 0;
371 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
374 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
375 guint32 (*start_func)(void *);
376 struct StartInfo *start_info;
384 g_message(G_GNUC_PRETTY_FUNCTION
385 ": Trying to start a new thread: this (%p) start (%p)",
389 im = mono_get_delegate_invoke (start->vtable->klass);
390 if (mono_thread_callbacks)
391 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
393 start_func = mono_compile_method (im);
395 if(start_func==NULL) {
396 g_warning(G_GNUC_PRETTY_FUNCTION
397 ": Can't locate start method!");
400 /* This is freed in start_wrapper */
401 start_info = g_new0 (struct StartInfo, 1);
402 start_info->func = start_func;
403 start_info->this = delegate;
404 start_info->obj = this;
405 start_info->domain = mono_domain_get ();
407 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
408 if(this->start_notify==NULL) {
409 g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
413 thread=CreateThread(NULL, default_stacksize, start_wrapper, start_info,
414 CREATE_SUSPENDED, &tid);
416 g_warning(G_GNUC_PRETTY_FUNCTION
417 ": CreateThread error 0x%x", GetLastError());
424 /* Don't call handle_store() here, delay it to Start.
425 * We can't join a thread (trying to will just block
426 * forever) until it actually starts running, so don't
427 * store the handle till then.
431 g_message(G_GNUC_PRETTY_FUNCTION
432 ": Started thread ID %d (handle %p)", tid, thread);
439 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
445 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
449 CloseHandle (thread);
452 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
458 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
459 GetCurrentThreadId (), this, this->tid);
462 /* Only store the handle when the thread is about to be
463 * launched, to avoid the main thread deadlocking while trying
464 * to clean up a thread that will never be signalled.
468 if (mono_thread_callbacks)
469 (* mono_thread_callbacks->start_resume) (this->tid);
471 ResumeThread(thread);
473 if (mono_thread_callbacks)
474 (* mono_thread_callbacks->end_resume) (this->tid);
476 if(this->start_notify!=NULL) {
477 /* Wait for the thread to set up its TLS data etc, so
478 * theres no potential race condition if someone tries
479 * to look up the data believing the thread has
484 g_message(G_GNUC_PRETTY_FUNCTION
485 ": (%d) waiting for thread %p (%d) to start",
486 GetCurrentThreadId (), this, this->tid);
489 WaitForSingleObject (this->start_notify, INFINITE);
490 CloseHandle (this->start_notify);
491 this->start_notify=NULL;
495 g_message(G_GNUC_PRETTY_FUNCTION
496 ": (%d) Done launching thread %p (%d)",
497 GetCurrentThreadId (), this, this->tid);
501 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
506 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
513 ves_icall_System_Threading_Thread_GetDomainID (void)
517 return mono_domain_get()->domain_id;
521 mono_thread_current (void)
527 /* Find the current thread object */
528 thread=TlsGetValue (current_object_key);
531 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
537 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
538 int ms, HANDLE thread)
548 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
552 ret=WaitForSingleObject(thread, ms);
553 if(ret==WAIT_OBJECT_0) {
555 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
562 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
568 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
573 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
576 /* Object location stored here */
577 TlsSetValue(slothash_key, data);
580 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
586 data=TlsGetValue(slothash_key);
589 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
595 /* FIXME: exitContext isnt documented */
596 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
602 MonoObject *waitHandle;
607 numhandles = mono_array_length(mono_handles);
608 handles = g_new0(HANDLE, numhandles);
610 if (wait_handle_os_handle_field == 0) {
611 /* Get the field os_handle which will contain the actual handle */
612 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
613 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
616 for(i = 0; i < numhandles; i++) {
617 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
618 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
625 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
629 if(ret==WAIT_FAILED) {
630 #ifdef THREAD_WAIT_DEBUG
631 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
632 GetCurrentThreadId ());
635 } else if(ret==WAIT_TIMEOUT) {
636 #ifdef THREAD_WAIT_DEBUG
637 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
638 GetCurrentThreadId ());
646 /* FIXME: exitContext isnt documented */
647 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
653 MonoObject *waitHandle;
658 numhandles = mono_array_length(mono_handles);
659 handles = g_new0(HANDLE, numhandles);
661 if (wait_handle_os_handle_field == 0) {
662 /* Get the field os_handle which will contain the actual handle */
663 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
664 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
667 for(i = 0; i < numhandles; i++) {
668 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
669 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
676 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
680 #ifdef THREAD_WAIT_DEBUG
681 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
682 GetCurrentThreadId (), ret);
686 * These need to be here. See MSDN dos on WaitForMultipleObjects.
688 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
689 return ret - WAIT_OBJECT_0;
691 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
692 return ret - WAIT_ABANDONED_0;
699 /* FIXME: exitContext isnt documented */
700 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
706 #ifdef THREAD_WAIT_DEBUG
707 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
708 GetCurrentThreadId (), handle, ms);
715 ret=WaitForSingleObject(handle, ms);
716 if(ret==WAIT_FAILED) {
717 #ifdef THREAD_WAIT_DEBUG
718 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
719 GetCurrentThreadId ());
722 } else if(ret==WAIT_TIMEOUT) {
723 #ifdef THREAD_WAIT_DEBUG
724 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
725 GetCurrentThreadId ());
733 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
736 return(CreateMutex(NULL,owned,name));
739 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
742 ReleaseMutex(handle);
745 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, char *name) {
748 return (CreateEvent(NULL,manual,initial,name));
751 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
754 return (SetEvent(handle));
757 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
760 return (ResetEvent(handle));
764 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
767 CloseHandle (handle);
770 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
774 return InterlockedIncrement (location);
777 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
784 EnterCriticalSection(&interlocked_mutex);
786 lowret = InterlockedIncrement((gint32 *) location);
788 highret = InterlockedIncrement((gint32 *) location + 1);
790 highret = *((gint32 *) location + 1);
792 LeaveCriticalSection(&interlocked_mutex);
794 return (gint64) highret << 32 | (gint64) lowret;
797 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
801 return InterlockedDecrement(location);
804 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
811 EnterCriticalSection(&interlocked_mutex);
813 lowret = InterlockedDecrement((gint32 *) location);
815 highret = InterlockedDecrement((gint32 *) location + 1);
817 highret = *((gint32 *) location + 1);
819 LeaveCriticalSection(&interlocked_mutex);
821 return (gint64) highret << 32 | (gint64) lowret;
824 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
828 return InterlockedExchange(location1, value);
831 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
835 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
838 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
840 IntFloatUnion val, ret;
845 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
850 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
854 return InterlockedCompareExchange(location1, value, comparand);
857 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
861 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
864 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
866 IntFloatUnion val, ret, cmp;
871 cmp.fval = comparand;
872 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
878 mono_thread_get_abort_signal (void)
888 #endif /* __MINGW32__ */
892 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
896 thread->abort_state = state;
897 thread->abort_exc = NULL;
900 g_message (G_GNUC_PRETTY_FUNCTION
901 ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
902 thread, thread->tid);
906 g_assert_not_reached ();
908 /* fixme: store the state somewhere */
909 #ifdef PTHREAD_POINTER_ID
910 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
912 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
914 #endif /* __MINGW32__ */
918 ves_icall_System_Threading_Thread_ResetAbort (void)
920 MonoThread *thread = mono_thread_current ();
924 if (!thread->abort_exc) {
925 const char *msg = "Unable to reset abort because no abort was requested";
926 mono_raise_exception (mono_get_exception_thread_state (msg));
928 thread->abort_exc = NULL;
929 thread->abort_state = NULL;
933 void mono_thread_init (MonoThreadStartCB start_cb,
934 MonoThreadAttachCB attach_cb)
936 InitializeCriticalSection(&threads_mutex);
937 InitializeCriticalSection(&interlocked_mutex);
939 current_object_key=TlsAlloc();
941 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
945 mono_thread_start_cb = start_cb;
946 mono_thread_attach_cb = attach_cb;
948 slothash_key=TlsAlloc();
950 /* Get a pseudo handle to the current process. This is just a
951 * kludge so that wapi can build a process handle if needed.
952 * As a pseudo handle is returned, we don't need to clean
955 GetCurrentProcess ();
959 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
961 mono_thread_cleanup = func;
964 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
966 mono_thread_callbacks = callbacks;
970 static void print_tids (gpointer key, gpointer value, gpointer user)
972 g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
978 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
979 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
983 static void wait_for_tids (struct wait_data *wait, guint32 timeout)
988 g_message(G_GNUC_PRETTY_FUNCTION
989 ": %d threads to wait for in this batch", wait->num);
992 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, timeout);
993 if(ret==WAIT_FAILED) {
994 /* See the comment in build_wait_tids() */
996 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
1002 for(i=0; i<wait->num; i++) {
1003 guint32 tid=wait->threads[i]->tid;
1005 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
1006 /* This thread must have been killed, because
1007 * it hasn't cleaned itself up. (It's just
1008 * possible that the thread exited before the
1009 * parent thread had a chance to store the
1010 * handle, and now there is another pointer to
1011 * the already-exited thread stored. In this
1012 * case, we'll just get two
1013 * mono_profiler_thread_end() calls for the
1018 g_message (G_GNUC_PRETTY_FUNCTION
1019 ": cleaning up after thread %d", tid);
1021 thread_cleanup (wait->threads[i]);
1026 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1028 struct wait_data *wait=(struct wait_data *)user;
1030 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1031 MonoThread *thread=(MonoThread *)value;
1033 /* BUG: For now we just ignore background threads, we should abort them
1035 if (thread->state & ThreadState_Background)
1036 return; /* just leave, ignore */
1038 wait->handles[wait->num]=thread->handle;
1039 wait->threads[wait->num]=thread;
1042 /* Just ignore the rest, we can't do anything with
1048 void mono_thread_manage (void)
1050 struct wait_data *wait=g_new0 (struct wait_data, 1);
1052 /* join each thread that's still running */
1054 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1059 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1065 EnterCriticalSection (&threads_mutex);
1067 g_message(G_GNUC_PRETTY_FUNCTION
1068 ":There are %d threads to join",
1069 mono_g_hash_table_size (threads));
1070 mono_g_hash_table_foreach (threads, print_tids, NULL);
1074 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1076 LeaveCriticalSection (&threads_mutex);
1078 /* Something to wait for */
1079 wait_for_tids (wait, INFINITE);
1081 } while(wait->num>0);
1085 mono_g_hash_table_destroy(threads);
1089 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1091 MonoThread *thread=(MonoThread *)value;
1092 guint32 self=GPOINTER_TO_UINT (user);
1094 if(thread->tid!=self) {
1095 /*TerminateThread (thread->handle, -1);*/
1099 void mono_thread_abort_all_other_threads (void)
1101 guint32 self=GetCurrentThreadId ();
1103 EnterCriticalSection (&threads_mutex);
1105 g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1106 mono_g_hash_table_size (threads));
1107 mono_g_hash_table_foreach (threads, print_tids, NULL);
1110 mono_g_hash_table_foreach (threads, terminate_thread,
1111 GUINT_TO_POINTER (self));
1113 LeaveCriticalSection (&threads_mutex);
1117 * mono_thread_push_appdomain_ref:
1119 * Register that the current thread may have references to objects in domain
1120 * @domain on its stack. Each call to this function should be paired with a
1121 * call to pop_appdomain_ref.
1124 mono_thread_push_appdomain_ref (MonoDomain *domain)
1126 MonoThread *thread = mono_thread_current ();
1129 //printf ("PUSH REF: %p -> %s.\n", thread, domain->friendly_name);
1130 EnterCriticalSection (&threads_mutex);
1131 thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain);
1132 LeaveCriticalSection (&threads_mutex);
1137 mono_thread_pop_appdomain_ref (void)
1139 MonoThread *thread = mono_thread_current ();
1142 //printf ("POP REF: %p -> %s.\n", thread, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name);
1143 EnterCriticalSection (&threads_mutex);
1144 thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data);
1145 LeaveCriticalSection (&threads_mutex);
1150 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
1153 EnterCriticalSection (&threads_mutex);
1154 res = g_slist_find (thread->appdomain_refs, domain) != NULL;
1155 LeaveCriticalSection (&threads_mutex);
1159 typedef struct abort_appdomain_data {
1160 struct wait_data wait;
1162 } abort_appdomain_data;
1165 abort_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
1167 MonoThread *thread = (MonoThread*)value;
1168 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
1169 MonoDomain *domain = data->domain;
1171 if (mono_thread_has_appdomain_ref (thread, domain)) {
1172 //printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread, domain->friendly_name);
1173 ves_icall_System_Threading_Thread_Abort (thread, (MonoObject*)domain->domain);
1175 if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
1176 data->wait.handles [data->wait.num] = thread->handle;
1177 data->wait.threads [data->wait.num] = thread;
1180 /* Just ignore the rest, we can't do anything with
1188 * mono_threads_abort_appdomain_threads:
1190 * Abort threads which has references to the given appdomain.
1193 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
1195 abort_appdomain_data user_data;
1198 //printf ("ABORT BEGIN.\n");
1200 start_time = GetTickCount ();
1202 EnterCriticalSection (&threads_mutex);
1204 user_data.domain = domain;
1205 user_data.wait.num = 0;
1206 mono_g_hash_table_foreach (threads, abort_appdomain_thread, &user_data);
1207 LeaveCriticalSection (&threads_mutex);
1209 if (user_data.wait.num > 0)
1210 wait_for_tids (&user_data.wait, timeout);
1212 /* Update remaining time */
1213 timeout -= GetTickCount () - start_time;
1214 start_time = GetTickCount ();
1219 while (user_data.wait.num > 0);
1221 //printf ("ABORT DONE.\n");
1227 * mono_thread_get_pending_exception:
1229 * Return an exception which needs to be raised when leaving a catch clause.
1230 * This is used for undeniable exception propagation.
1233 mono_thread_get_pending_exception (void)
1235 MonoThread *thread = mono_thread_current ();
1237 MONO_ARCH_SAVE_REGS;
1239 if (thread && thread->abort_exc) {
1241 * FIXME: Clear the abort exception and return an AppDomainUnloaded
1242 * exception if the thread no longer references a dying appdomain.
1244 thread->abort_exc->trace_ips = NULL;
1245 thread->abort_exc->stack_trace = NULL;
1246 return thread->abort_exc;
1252 static int static_data_idx = 0;
1253 static int static_data_offset = 0;
1254 #define NUM_STATIC_DATA_IDX 8
1255 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
1256 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
1260 thread_alloc_static_data (MonoThread *thread, guint32 offset)
1262 guint idx = (offset >> 24) - 1;
1265 if (!thread->static_data) {
1267 thread->static_data = GC_MALLOC (static_data_size [0]);
1269 thread->static_data = g_malloc0 (static_data_size [0]);
1271 thread->static_data [0] = thread->static_data;
1273 for (i = 1; i < idx; ++i) {
1274 if (thread->static_data [i])
1277 thread->static_data [i] = GC_MALLOC (static_data_size [i]);
1279 thread->static_data [i] = g_malloc0 (static_data_size [i]);
1286 * ensure thread static fields already allocated are valid for thread
1287 * This function is called when a thread is created or on thread attach.
1290 thread_adjust_static_data (MonoThread *thread)
1294 EnterCriticalSection (&threads_mutex);
1295 if (static_data_offset || static_data_idx > 0) {
1296 /* get the current allocated size */
1297 offset = static_data_offset | ((static_data_idx + 1) << 24);
1298 thread_alloc_static_data (thread, offset);
1300 LeaveCriticalSection (&threads_mutex);
1304 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
1306 MonoThread *thread = value;
1307 guint32 offset = GPOINTER_TO_UINT (user);
1309 thread_alloc_static_data (thread, offset);
1313 * The offset for a thread static variable is composed of two parts:
1314 * an index in the array of chunks of memory for the thread (thread->static_data)
1315 * and an offset in that chunk of mem. This allows allocating less memory in the
1319 mono_threads_alloc_static_data (guint32 size, guint32 align)
1323 EnterCriticalSection (&threads_mutex);
1325 if (!static_data_idx && !static_data_offset) {
1327 * we use the first chunk of the first allocation also as
1328 * an array for the rest of the data
1330 static_data_offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
1332 static_data_offset += align - 1;
1333 static_data_offset &= ~(align - 1);
1334 if (static_data_offset + size >= static_data_size [static_data_idx]) {
1336 g_assert (size <= static_data_size [static_data_idx]);
1338 * massive unloading and reloading of domains with thread-static
1339 * data may eventually exceed the allocated storage...
1340 * Need to check what the MS runtime does in that case.
1341 * Note that for each appdomain, we need to allocate a separate
1342 * thread data slot for security reasons. We could keep track
1343 * of the slots per-domain and when the domain is unloaded
1344 * out the slots on a sort of free list.
1346 g_assert (static_data_idx < NUM_STATIC_DATA_IDX);
1347 static_data_offset = 0;
1349 offset = static_data_offset | ((static_data_idx + 1) << 24);
1350 static_data_offset += size;
1352 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
1354 LeaveCriticalSection (&threads_mutex);
1359 mono_threads_get_static_data (guint32 offset)
1361 MonoThread *thread = mono_thread_current ();
1362 int idx = offset >> 24;
1364 return ((char*) thread->static_data [idx - 1]) + (offset & 0xffffff);
1367 static void gc_stop_world (gpointer key, gpointer value, gpointer user)
1369 MonoThread *thread=(MonoThread *)value;
1370 guint32 self=GPOINTER_TO_UINT (user);
1373 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1376 if(thread->tid==self)
1379 SuspendThread (thread->handle);
1382 void mono_gc_stop_world (void)
1384 guint32 self=GetCurrentThreadId ();
1387 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1390 EnterCriticalSection (&threads_mutex);
1392 if (threads != NULL)
1393 mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
1395 LeaveCriticalSection (&threads_mutex);
1398 static void gc_start_world (gpointer key, gpointer value, gpointer user)
1400 MonoThread *thread=(MonoThread *)value;
1401 guint32 self=GPOINTER_TO_UINT (user);
1404 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1407 if(thread->tid==self)
1410 ResumeThread (thread->handle);
1413 void mono_gc_start_world (void)
1415 guint32 self=GetCurrentThreadId ();
1418 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1421 EnterCriticalSection (&threads_mutex);
1423 if (threads != NULL)
1424 mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
1426 LeaveCriticalSection (&threads_mutex);
1429 #ifdef WITH_INCLUDED_LIBGC
1431 static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
1433 MonoThread *thread=(MonoThread *)value;
1434 guint32 *selfp=(guint32 *)user, self = *selfp;
1437 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
1440 if(thread->tid==self) {
1442 g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
1444 GC_push_all_stack (selfp, thread->stack_ptr);
1448 #ifdef PLATFORM_WIN32
1449 GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
1451 mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
1455 void mono_gc_push_all_stacks (void)
1457 guint32 self=GetCurrentThreadId ();
1460 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1463 EnterCriticalSection (&threads_mutex);
1465 if (threads != NULL)
1466 mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
1468 LeaveCriticalSection (&threads_mutex);
1471 #endif /* WITH_INCLUDED_LIBGC */