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/threads-types.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/metadata/environment.h>
23 #include <mono/metadata/monitor.h>
24 #include <mono/io-layer/io-layer.h>
26 #include <mono/os/gc_wrapper.h>
29 #undef THREAD_WAIT_DEBUG
33 guint32 (*func)(void *);
45 * The "os_handle" field of the WaitHandle class.
47 static MonoClassField *wait_handle_os_handle_field = NULL;
49 /* Controls access to the 'threads' hash table */
50 static CRITICAL_SECTION threads_mutex;
52 /* The hash of existing threads (key is thread ID) that need joining
55 static MonoGHashTable *threads=NULL;
57 /* The TLS key that holds the MonoObject assigned to each thread */
58 static guint32 current_object_key;
60 /* function called at thread start */
61 static MonoThreadStartCB mono_thread_start_cb = NULL;
63 /* function called at thread attach */
64 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
66 /* function called when a new thread has been created */
67 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
69 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
70 static guint32 slothash_key;
72 static void thread_adjust_static_data (MonoThread *thread);
74 /* Spin lock for InterlockedXXX 64 bit functions */
75 static CRITICAL_SECTION interlocked_mutex;
77 /* handle_store() and handle_remove() manage the array of threads that
78 * still need to be waited for when the main thread exits.
80 static void handle_store(MonoThread *thread)
82 EnterCriticalSection(&threads_mutex);
85 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
90 threads=mono_g_hash_table_new(g_direct_hash, g_direct_equal);
93 /* We don't need to duplicate thread->handle, because it is
94 * only closed when the thread object is finalized by the GC.
96 mono_g_hash_table_insert(threads, GUINT_TO_POINTER(thread->tid), thread);
97 LeaveCriticalSection(&threads_mutex);
100 static void handle_remove(guint32 tid)
103 g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
106 EnterCriticalSection(&threads_mutex);
108 mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid));
110 LeaveCriticalSection(&threads_mutex);
112 /* Don't close the handle here, wait for the object finalizer
113 * to do it. Otherwise, the following race condition applies:
115 * 1) Thread exits (and handle_remove() closes the handle)
117 * 2) Some other handle is reassigned the same slot
119 * 3) Another thread tries to join the first thread, and
120 * blocks waiting for the reassigned handle to be signalled
121 * (which might never happen). This is possible, because the
122 * thread calling Join() still has a reference to the first
127 static void thread_cleanup (MonoThread *thread)
129 mono_monitor_try_enter ((MonoObject *)thread, INFINITE);
130 thread->state |= ThreadState_Stopped;
131 mono_monitor_exit ((MonoObject *)thread);
133 mono_profiler_thread_end (thread->tid);
134 handle_remove (thread->tid);
137 static guint32 start_wrapper(void *data)
139 struct StartInfo *start_info=(struct StartInfo *)data;
140 guint32 (*start_func)(void *);
143 MonoThread *thread=start_info->obj;
146 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
147 GetCurrentThreadId ());
150 /* We can be sure start_info->obj->tid and
151 * start_info->obj->handle have been set, because the thread
152 * was created suspended, and these values were set before the
158 mono_domain_set (start_info->domain);
160 start_func = start_info->func;
161 this = start_info->this;
163 /* This MUST be called before any managed code can be
164 * executed, as it calls the callback function that (for the
165 * jit) sets the lmf marker.
167 mono_thread_new_init (tid, &tid, start_func);
168 thread->stack_ptr = &tid;
171 g_message (G_GNUC_PRETTY_FUNCTION
172 ": (%d,%d) Setting thread stack to %p",
173 GetCurrentThreadId (), getpid (), thread->stack_ptr);
177 g_message (G_GNUC_PRETTY_FUNCTION
178 ": (%d) Setting current_object_key to %p",
179 GetCurrentThreadId (), thread);
182 TlsSetValue (current_object_key, thread);
184 mono_profiler_thread_start (tid);
186 if(thread->start_notify!=NULL) {
187 /* Let the thread that called Start() know we're
190 ReleaseSemaphore (thread->start_notify, 1, NULL);
195 thread_adjust_static_data (thread);
199 /* If the thread calls ExitThread at all, this remaining code
200 * will not be executed, but the main thread will eventually
201 * call thread_cleanup() on this thread's behalf.
205 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
206 GetCurrentThreadId ());
209 thread_cleanup (thread);
214 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
216 if (mono_thread_start_cb) {
217 mono_thread_start_cb (tid, stack_start, func);
220 if (mono_thread_callbacks)
221 (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
224 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
227 HANDLE thread_handle;
228 struct StartInfo *start_info;
231 thread=(MonoThread *)mono_object_new (domain,
232 mono_defaults.thread_class);
234 start_info=g_new0 (struct StartInfo, 1);
235 start_info->func = func;
236 start_info->obj = thread;
237 start_info->domain = domain;
238 start_info->this = arg;
240 /* Create suspended, so we can do some housekeeping before the thread
243 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info,
244 CREATE_SUSPENDED, &tid);
246 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
249 g_assert (thread_handle);
251 thread->handle=thread_handle;
254 handle_store(thread);
256 ResumeThread (thread_handle);
260 mono_thread_attach (MonoDomain *domain)
263 HANDLE thread_handle;
266 if ((thread = mono_thread_current ())) {
267 g_warning ("mono_thread_attach called for an already attached thread");
268 if (mono_thread_attach_cb) {
269 mono_thread_attach_cb (tid, &tid);
274 thread = (MonoThread *)mono_object_new (domain,
275 mono_defaults.thread_class);
277 thread_handle = GetCurrentThread ();
278 g_assert (thread_handle);
280 tid=GetCurrentThreadId ();
282 thread->handle=thread_handle;
286 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
290 handle_store(thread);
293 g_message (G_GNUC_PRETTY_FUNCTION
294 ": (%d) Setting current_object_key to %p",
295 GetCurrentThreadId (), thread);
298 TlsSetValue (current_object_key, thread);
299 mono_domain_set (domain);
301 thread_adjust_static_data (thread);
303 if (mono_thread_attach_cb) {
304 mono_thread_attach_cb (tid, &tid);
310 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
313 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
314 guint32 (*start_func)(void *);
315 struct StartInfo *start_info;
323 g_message(G_GNUC_PRETTY_FUNCTION
324 ": Trying to start a new thread: this (%p) start (%p)",
328 im = mono_get_delegate_invoke (start->vtable->klass);
329 if (mono_thread_callbacks)
330 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
332 start_func = mono_compile_method (im);
334 if(start_func==NULL) {
335 g_warning(G_GNUC_PRETTY_FUNCTION
336 ": Can't locate start method!");
339 /* This is freed in start_wrapper */
340 start_info = g_new0 (struct StartInfo, 1);
341 start_info->func = start_func;
342 start_info->this = delegate;
343 start_info->obj = this;
344 start_info->domain = mono_domain_get ();
346 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
347 if(this->start_notify==NULL) {
348 g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
352 thread=CreateThread(NULL, 0, start_wrapper, start_info,
353 CREATE_SUSPENDED, &tid);
355 g_warning(G_GNUC_PRETTY_FUNCTION
356 ": CreateThread error 0x%x", GetLastError());
363 /* Don't call handle_store() here, delay it to Start.
364 * We can't join a thread (trying to will just block
365 * forever) until it actually starts running, so don't
366 * store the handle till then.
370 g_message(G_GNUC_PRETTY_FUNCTION
371 ": Started thread ID %d (handle %p)", tid, thread);
378 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
384 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
388 CloseHandle (thread);
391 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
397 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
398 GetCurrentThreadId (), this, this->tid);
401 /* Only store the handle when the thread is about to be
402 * launched, to avoid the main thread deadlocking while trying
403 * to clean up a thread that will never be signalled.
407 if (mono_thread_callbacks)
408 (* mono_thread_callbacks->start_resume) (this->tid);
410 ResumeThread(thread);
412 if (mono_thread_callbacks)
413 (* mono_thread_callbacks->end_resume) (this->tid);
415 if(this->start_notify!=NULL) {
416 /* Wait for the thread to set up its TLS data etc, so
417 * theres no potential race condition if someone tries
418 * to look up the data believing the thread has
423 g_message(G_GNUC_PRETTY_FUNCTION
424 ": (%d) waiting for thread %p (%d) to start",
425 GetCurrentThreadId (), this, this->tid);
428 WaitForSingleObject (this->start_notify, INFINITE);
429 CloseHandle (this->start_notify);
430 this->start_notify=NULL;
434 g_message(G_GNUC_PRETTY_FUNCTION
435 ": (%d) Done launching thread %p (%d)",
436 GetCurrentThreadId (), this, this->tid);
440 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
445 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
452 ves_icall_System_Threading_Thread_GetDomainID (void)
456 return mono_domain_get()->domain_id;
460 mono_thread_current (void)
466 /* Find the current thread object */
467 thread=TlsGetValue (current_object_key);
470 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
476 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
477 int ms, HANDLE thread)
487 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
491 ret=WaitForSingleObject(thread, ms);
492 if(ret==WAIT_OBJECT_0) {
494 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
501 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
507 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
512 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
515 /* Object location stored here */
516 TlsSetValue(slothash_key, data);
519 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
525 data=TlsGetValue(slothash_key);
528 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
534 /* FIXME: exitContext isnt documented */
535 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
541 MonoObject *waitHandle;
546 numhandles = mono_array_length(mono_handles);
547 handles = g_new0(HANDLE, numhandles);
549 if (wait_handle_os_handle_field == 0) {
550 /* Get the field os_handle which will contain the actual handle */
551 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
552 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
555 for(i = 0; i < numhandles; i++) {
556 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
557 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
564 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
568 if(ret==WAIT_FAILED) {
569 #ifdef THREAD_WAIT_DEBUG
570 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
571 GetCurrentThreadId ());
574 } else if(ret==WAIT_TIMEOUT) {
575 #ifdef THREAD_WAIT_DEBUG
576 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
577 GetCurrentThreadId ());
585 /* FIXME: exitContext isnt documented */
586 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
592 MonoObject *waitHandle;
597 numhandles = mono_array_length(mono_handles);
598 handles = g_new0(HANDLE, numhandles);
600 if (wait_handle_os_handle_field == 0) {
601 /* Get the field os_handle which will contain the actual handle */
602 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
603 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
606 for(i = 0; i < numhandles; i++) {
607 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
608 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
615 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
619 #ifdef THREAD_WAIT_DEBUG
620 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
621 GetCurrentThreadId (), ret);
625 * These need to be here. See MSDN dos on WaitForMultipleObjects.
627 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
628 return ret - WAIT_OBJECT_0;
630 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
631 return ret - WAIT_ABANDONED_0;
638 /* FIXME: exitContext isnt documented */
639 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
645 #ifdef THREAD_WAIT_DEBUG
646 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
647 GetCurrentThreadId (), handle, ms);
654 ret=WaitForSingleObject(handle, ms);
655 if(ret==WAIT_FAILED) {
656 #ifdef THREAD_WAIT_DEBUG
657 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
658 GetCurrentThreadId ());
661 } else if(ret==WAIT_TIMEOUT) {
662 #ifdef THREAD_WAIT_DEBUG
663 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
664 GetCurrentThreadId ());
672 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
675 return(CreateMutex(NULL,owned,name));
678 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
681 ReleaseMutex(handle);
684 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
689 return (CreateEvent(NULL,manual,initial,name));
692 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
695 return (SetEvent(handle));
698 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
701 return (ResetEvent(handle));
704 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
708 return InterlockedIncrement (location);
711 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
718 EnterCriticalSection(&interlocked_mutex);
720 lowret = InterlockedIncrement((gint32 *) location);
722 highret = InterlockedIncrement((gint32 *) location + 1);
724 highret = *((gint32 *) location + 1);
726 LeaveCriticalSection(&interlocked_mutex);
728 return (gint64) highret << 32 | (gint64) lowret;
731 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
735 return InterlockedDecrement(location);
738 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
745 EnterCriticalSection(&interlocked_mutex);
747 lowret = InterlockedDecrement((gint32 *) location);
749 highret = InterlockedDecrement((gint32 *) location + 1);
751 highret = *((gint32 *) location + 1);
753 LeaveCriticalSection(&interlocked_mutex);
755 return (gint64) highret << 32 | (gint64) lowret;
758 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
762 return InterlockedExchange(location1, value);
765 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
769 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
772 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
774 IntFloatUnion val, ret;
779 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
784 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
788 return InterlockedCompareExchange(location1, value, comparand);
791 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
795 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
798 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
800 IntFloatUnion val, ret, cmp;
805 cmp.fval = comparand;
806 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
812 mono_thread_get_abort_signal (void)
822 #endif /* __MINGW32__ */
826 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
830 thread->abort_state = state;
831 thread->abort_exc = mono_get_exception_thread_abort ();
834 g_message (G_GNUC_PRETTY_FUNCTION
835 ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
836 thread, thread->tid);
840 g_assert_not_reached ();
842 /* fixme: store the state somewhere */
843 #ifdef PTHREAD_POINTER_ID
844 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
846 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
848 #endif /* __MINGW32__ */
852 ves_icall_System_Threading_Thread_ResetAbort (void)
854 MonoThread *thread = mono_thread_current ();
858 if (!thread->abort_exc) {
859 const char *msg = "Unable to reset abort because no abort was requested";
860 mono_raise_exception (mono_get_exception_thread_state (msg));
862 thread->abort_exc = NULL;
863 thread->abort_state = NULL;
867 void mono_thread_init (MonoThreadStartCB start_cb,
868 MonoThreadAttachCB attach_cb)
870 InitializeCriticalSection(&threads_mutex);
871 InitializeCriticalSection(&interlocked_mutex);
873 current_object_key=TlsAlloc();
875 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
879 mono_thread_start_cb = start_cb;
880 mono_thread_attach_cb = attach_cb;
882 slothash_key=TlsAlloc();
884 /* Get a pseudo handle to the current process. This is just a
885 * kludge so that wapi can build a process handle if needed.
886 * As a pseudo handle is returned, we don't need to clean
889 GetCurrentProcess ();
892 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
894 mono_thread_callbacks = callbacks;
898 static void print_tids (gpointer key, gpointer value, gpointer user)
900 g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
906 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
907 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
911 static void wait_for_tids (struct wait_data *wait)
916 g_message(G_GNUC_PRETTY_FUNCTION
917 ": %d threads to wait for in this batch", wait->num);
920 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
921 if(ret==WAIT_FAILED) {
922 /* See the comment in build_wait_tids() */
924 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
930 for(i=0; i<wait->num; i++) {
931 guint32 tid=wait->threads[i]->tid;
933 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
934 /* This thread must have been killed, because
935 * it hasn't cleaned itself up. (It's just
936 * possible that the thread exited before the
937 * parent thread had a chance to store the
938 * handle, and now there is another pointer to
939 * the already-exited thread stored. In this
940 * case, we'll just get two
941 * mono_profiler_thread_end() calls for the
946 g_message (G_GNUC_PRETTY_FUNCTION
947 ": cleaning up after thread %d", tid);
949 thread_cleanup (wait->threads[i]);
954 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
956 struct wait_data *wait=(struct wait_data *)user;
958 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
959 MonoThread *thread=(MonoThread *)value;
961 /* BUG: For now we just ignore background threads, we should abort them
963 if (thread->state & ThreadState_Background)
964 return; /* just leave, ignore */
966 wait->handles[wait->num]=thread->handle;
967 wait->threads[wait->num]=thread;
970 /* Just ignore the rest, we can't do anything with
976 void mono_thread_manage (void)
978 struct wait_data *wait=g_new0 (struct wait_data, 1);
980 /* join each thread that's still running */
982 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
987 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
993 EnterCriticalSection (&threads_mutex);
995 g_message(G_GNUC_PRETTY_FUNCTION
996 ":There are %d threads to join",
997 mono_g_hash_table_size (threads));
998 mono_g_hash_table_foreach (threads, print_tids, NULL);
1002 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1004 LeaveCriticalSection (&threads_mutex);
1006 /* Something to wait for */
1007 wait_for_tids (wait);
1009 } while(wait->num>0);
1013 mono_g_hash_table_destroy(threads);
1017 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1019 MonoThread *thread=(MonoThread *)value;
1020 guint32 self=GPOINTER_TO_UINT (user);
1022 if(thread->tid!=self) {
1023 /*TerminateThread (thread->handle, -1);*/
1027 void mono_thread_abort_all_other_threads (void)
1029 guint32 self=GetCurrentThreadId ();
1031 EnterCriticalSection (&threads_mutex);
1033 g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1034 mono_g_hash_table_size (threads));
1035 mono_g_hash_table_foreach (threads, print_tids, NULL);
1038 mono_g_hash_table_foreach (threads, terminate_thread,
1039 GUINT_TO_POINTER (self));
1041 LeaveCriticalSection (&threads_mutex);
1044 static int static_data_idx = 0;
1045 static int static_data_offset = 0;
1046 #define NUM_STATIC_DATA_IDX 8
1047 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
1048 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
1052 thread_alloc_static_data (MonoThread *thread, guint32 offset)
1054 guint idx = (offset >> 24) - 1;
1057 if (!thread->static_data) {
1058 thread->static_data = GC_MALLOC (static_data_size [0]);
1059 thread->static_data [0] = thread->static_data;
1061 for (i = 1; i < idx; ++i) {
1062 if (thread->static_data [i])
1064 thread->static_data [i] = GC_MALLOC (static_data_size [i]);
1070 * ensure thread static fields already allocated are valid for thread
1071 * This function is called when a thread is created or on thread attach.
1074 thread_adjust_static_data (MonoThread *thread)
1078 EnterCriticalSection (&threads_mutex);
1079 if (static_data_offset || static_data_idx > 0) {
1080 /* get the current allocated size */
1081 offset = static_data_offset | ((static_data_idx + 1) << 24);
1082 thread_alloc_static_data (thread, offset);
1084 LeaveCriticalSection (&threads_mutex);
1088 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
1090 MonoThread *thread = value;
1091 guint32 offset = GPOINTER_TO_UINT (user);
1093 thread_alloc_static_data (thread, offset);
1097 * The offset for a thread static variable is composed of two parts:
1098 * an index in the array of chunks of memory for the thread (thread->static_data)
1099 * and an offset in that chunk of mem. This allows allocating less memory in the
1103 mono_threads_alloc_static_data (guint32 size, guint32 align)
1107 EnterCriticalSection (&threads_mutex);
1109 if (!static_data_idx && !static_data_offset) {
1111 * we use the first chunk of the first allocation also as
1112 * an array for the rest of the data
1114 static_data_offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
1116 static_data_offset += align - 1;
1117 static_data_offset &= ~(align - 1);
1118 if (static_data_offset + size >= static_data_size [static_data_idx]) {
1120 g_assert (size <= static_data_size [static_data_idx]);
1122 * massive unloading and reloading of domains with thread-static
1123 * data may eventually exceed the allocated storage...
1124 * Need to check what the MS runtime does in that case.
1125 * Note that for each appdomain, we need to allocate a separate
1126 * thread data slot for security reasons. We could keep track
1127 * of the slots per-domain and when the domain is unloaded
1128 * out the slots on a sort of free list.
1130 g_assert (static_data_idx < NUM_STATIC_DATA_IDX);
1131 static_data_offset = 0;
1133 offset = static_data_offset | ((static_data_idx + 1) << 24);
1134 static_data_offset += size;
1136 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
1138 LeaveCriticalSection (&threads_mutex);
1143 mono_threads_get_static_data (guint32 offset)
1145 MonoThread *thread = mono_thread_current ();
1146 int idx = offset >> 24;
1148 return ((char*) thread->static_data [idx - 1]) + (offset & 0xffffff);
1151 #ifdef WITH_INCLUDED_LIBGC
1153 static void gc_stop_world (gpointer key, gpointer value, gpointer user)
1155 MonoThread *thread=(MonoThread *)value;
1156 guint32 self=GPOINTER_TO_UINT (user);
1159 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1162 if(thread->tid==self)
1165 SuspendThread (thread->handle);
1168 void mono_gc_stop_world (void)
1170 guint32 self=GetCurrentThreadId ();
1173 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1176 EnterCriticalSection (&threads_mutex);
1178 if (threads != NULL)
1179 mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
1181 LeaveCriticalSection (&threads_mutex);
1184 static void gc_start_world (gpointer key, gpointer value, gpointer user)
1186 MonoThread *thread=(MonoThread *)value;
1187 guint32 self=GPOINTER_TO_UINT (user);
1190 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1193 if(thread->tid==self)
1196 ResumeThread (thread->handle);
1199 void mono_gc_start_world (void)
1201 guint32 self=GetCurrentThreadId ();
1204 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1207 EnterCriticalSection (&threads_mutex);
1209 if (threads != NULL)
1210 mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
1212 LeaveCriticalSection (&threads_mutex);
1215 static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
1217 MonoThread *thread=(MonoThread *)value;
1218 guint32 *selfp=(guint32 *)user, self = *selfp;
1221 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
1224 if(thread->tid==self) {
1226 g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
1228 GC_push_all_stack (selfp, thread->stack_ptr);
1232 #ifdef PLATFORM_WIN32
1233 GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
1235 mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
1239 void mono_gc_push_all_stacks (void)
1241 guint32 self=GetCurrentThreadId ();
1244 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1247 EnterCriticalSection (&threads_mutex);
1249 if (threads != NULL)
1250 mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
1252 LeaveCriticalSection (&threads_mutex);
1255 #endif /* WITH_INCLUDED_LIBGC */