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 /* Remove the reference to the thread object in the TLS data,
210 * so the thread object can be finalized. This won't be
211 * reached if the thread threw an uncaught exception, so those
212 * thread handles will stay referenced :-( (This is due to
213 * missing support for scanning thread-specific data in the
214 * Boehm GC - the io-layer keeps a GC-visible hash of pointers
217 TlsSetValue (current_object_key, NULL);
219 thread_cleanup (thread);
224 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
226 if (mono_thread_start_cb) {
227 mono_thread_start_cb (tid, stack_start, func);
230 if (mono_thread_callbacks)
231 (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
234 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
237 HANDLE thread_handle;
238 struct StartInfo *start_info;
241 thread=(MonoThread *)mono_object_new (domain,
242 mono_defaults.thread_class);
244 start_info=g_new0 (struct StartInfo, 1);
245 start_info->func = func;
246 start_info->obj = thread;
247 start_info->domain = domain;
248 start_info->this = arg;
250 /* Create suspended, so we can do some housekeeping before the thread
253 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info,
254 CREATE_SUSPENDED, &tid);
256 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
259 g_assert (thread_handle);
261 thread->handle=thread_handle;
264 handle_store(thread);
266 ResumeThread (thread_handle);
270 mono_thread_attach (MonoDomain *domain)
273 HANDLE thread_handle;
276 if ((thread = mono_thread_current ())) {
277 g_warning ("mono_thread_attach called for an already attached thread");
278 if (mono_thread_attach_cb) {
279 mono_thread_attach_cb (tid, &tid);
284 thread = (MonoThread *)mono_object_new (domain,
285 mono_defaults.thread_class);
287 thread_handle = GetCurrentThread ();
288 g_assert (thread_handle);
290 tid=GetCurrentThreadId ();
292 thread->handle=thread_handle;
296 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
300 handle_store(thread);
303 g_message (G_GNUC_PRETTY_FUNCTION
304 ": (%d) Setting current_object_key to %p",
305 GetCurrentThreadId (), thread);
308 TlsSetValue (current_object_key, thread);
309 mono_domain_set (domain);
311 thread_adjust_static_data (thread);
313 if (mono_thread_attach_cb) {
314 mono_thread_attach_cb (tid, &tid);
320 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
323 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
324 guint32 (*start_func)(void *);
325 struct StartInfo *start_info;
333 g_message(G_GNUC_PRETTY_FUNCTION
334 ": Trying to start a new thread: this (%p) start (%p)",
338 im = mono_get_delegate_invoke (start->vtable->klass);
339 if (mono_thread_callbacks)
340 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
342 start_func = mono_compile_method (im);
344 if(start_func==NULL) {
345 g_warning(G_GNUC_PRETTY_FUNCTION
346 ": Can't locate start method!");
349 /* This is freed in start_wrapper */
350 start_info = g_new0 (struct StartInfo, 1);
351 start_info->func = start_func;
352 start_info->this = delegate;
353 start_info->obj = this;
354 start_info->domain = mono_domain_get ();
356 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
357 if(this->start_notify==NULL) {
358 g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
362 thread=CreateThread(NULL, 0, start_wrapper, start_info,
363 CREATE_SUSPENDED, &tid);
365 g_warning(G_GNUC_PRETTY_FUNCTION
366 ": CreateThread error 0x%x", GetLastError());
373 /* Don't call handle_store() here, delay it to Start.
374 * We can't join a thread (trying to will just block
375 * forever) until it actually starts running, so don't
376 * store the handle till then.
380 g_message(G_GNUC_PRETTY_FUNCTION
381 ": Started thread ID %d (handle %p)", tid, thread);
388 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
394 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
398 CloseHandle (thread);
401 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
407 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
408 GetCurrentThreadId (), this, this->tid);
411 /* Only store the handle when the thread is about to be
412 * launched, to avoid the main thread deadlocking while trying
413 * to clean up a thread that will never be signalled.
417 if (mono_thread_callbacks)
418 (* mono_thread_callbacks->start_resume) (this->tid);
420 ResumeThread(thread);
422 if (mono_thread_callbacks)
423 (* mono_thread_callbacks->end_resume) (this->tid);
425 if(this->start_notify!=NULL) {
426 /* Wait for the thread to set up its TLS data etc, so
427 * theres no potential race condition if someone tries
428 * to look up the data believing the thread has
433 g_message(G_GNUC_PRETTY_FUNCTION
434 ": (%d) waiting for thread %p (%d) to start",
435 GetCurrentThreadId (), this, this->tid);
438 WaitForSingleObject (this->start_notify, INFINITE);
439 CloseHandle (this->start_notify);
440 this->start_notify=NULL;
444 g_message(G_GNUC_PRETTY_FUNCTION
445 ": (%d) Done launching thread %p (%d)",
446 GetCurrentThreadId (), this, this->tid);
450 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
455 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
462 ves_icall_System_Threading_Thread_GetDomainID (void)
466 return mono_domain_get()->domain_id;
470 mono_thread_current (void)
476 /* Find the current thread object */
477 thread=TlsGetValue (current_object_key);
480 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
486 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
487 int ms, HANDLE thread)
497 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
501 ret=WaitForSingleObject(thread, ms);
502 if(ret==WAIT_OBJECT_0) {
504 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
511 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
517 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
522 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
525 /* Object location stored here */
526 TlsSetValue(slothash_key, data);
529 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
535 data=TlsGetValue(slothash_key);
538 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
544 /* FIXME: exitContext isnt documented */
545 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
551 MonoObject *waitHandle;
556 numhandles = mono_array_length(mono_handles);
557 handles = g_new0(HANDLE, numhandles);
559 if (wait_handle_os_handle_field == 0) {
560 /* Get the field os_handle which will contain the actual handle */
561 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
562 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
565 for(i = 0; i < numhandles; i++) {
566 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
567 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
574 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
578 if(ret==WAIT_FAILED) {
579 #ifdef THREAD_WAIT_DEBUG
580 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
581 GetCurrentThreadId ());
584 } else if(ret==WAIT_TIMEOUT) {
585 #ifdef THREAD_WAIT_DEBUG
586 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
587 GetCurrentThreadId ());
595 /* FIXME: exitContext isnt documented */
596 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_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, FALSE, ms);
629 #ifdef THREAD_WAIT_DEBUG
630 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
631 GetCurrentThreadId (), ret);
635 * These need to be here. See MSDN dos on WaitForMultipleObjects.
637 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
638 return ret - WAIT_OBJECT_0;
640 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
641 return ret - WAIT_ABANDONED_0;
648 /* FIXME: exitContext isnt documented */
649 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
655 #ifdef THREAD_WAIT_DEBUG
656 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
657 GetCurrentThreadId (), handle, ms);
664 ret=WaitForSingleObject(handle, ms);
665 if(ret==WAIT_FAILED) {
666 #ifdef THREAD_WAIT_DEBUG
667 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
668 GetCurrentThreadId ());
671 } else if(ret==WAIT_TIMEOUT) {
672 #ifdef THREAD_WAIT_DEBUG
673 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
674 GetCurrentThreadId ());
682 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
685 return(CreateMutex(NULL,owned,name));
688 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
691 ReleaseMutex(handle);
694 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, char *name) {
697 return (CreateEvent(NULL,manual,initial,name));
700 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
703 return (SetEvent(handle));
706 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
709 return (ResetEvent(handle));
713 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
716 CloseHandle (handle);
719 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
723 return InterlockedIncrement (location);
726 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
733 EnterCriticalSection(&interlocked_mutex);
735 lowret = InterlockedIncrement((gint32 *) location);
737 highret = InterlockedIncrement((gint32 *) location + 1);
739 highret = *((gint32 *) location + 1);
741 LeaveCriticalSection(&interlocked_mutex);
743 return (gint64) highret << 32 | (gint64) lowret;
746 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
750 return InterlockedDecrement(location);
753 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
760 EnterCriticalSection(&interlocked_mutex);
762 lowret = InterlockedDecrement((gint32 *) location);
764 highret = InterlockedDecrement((gint32 *) location + 1);
766 highret = *((gint32 *) location + 1);
768 LeaveCriticalSection(&interlocked_mutex);
770 return (gint64) highret << 32 | (gint64) lowret;
773 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
777 return InterlockedExchange(location1, value);
780 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
784 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
787 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
789 IntFloatUnion val, ret;
794 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
799 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
803 return InterlockedCompareExchange(location1, value, comparand);
806 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
810 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
813 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
815 IntFloatUnion val, ret, cmp;
820 cmp.fval = comparand;
821 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
827 mono_thread_get_abort_signal (void)
837 #endif /* __MINGW32__ */
841 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
845 thread->abort_state = state;
846 thread->abort_exc = mono_get_exception_thread_abort ();
849 g_message (G_GNUC_PRETTY_FUNCTION
850 ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
851 thread, thread->tid);
855 g_assert_not_reached ();
857 /* fixme: store the state somewhere */
858 #ifdef PTHREAD_POINTER_ID
859 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
861 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
863 #endif /* __MINGW32__ */
867 ves_icall_System_Threading_Thread_ResetAbort (void)
869 MonoThread *thread = mono_thread_current ();
873 if (!thread->abort_exc) {
874 const char *msg = "Unable to reset abort because no abort was requested";
875 mono_raise_exception (mono_get_exception_thread_state (msg));
877 thread->abort_exc = NULL;
878 thread->abort_state = NULL;
882 void mono_thread_init (MonoThreadStartCB start_cb,
883 MonoThreadAttachCB attach_cb)
885 InitializeCriticalSection(&threads_mutex);
886 InitializeCriticalSection(&interlocked_mutex);
888 current_object_key=TlsAlloc();
890 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
894 mono_thread_start_cb = start_cb;
895 mono_thread_attach_cb = attach_cb;
897 slothash_key=TlsAlloc();
899 /* Get a pseudo handle to the current process. This is just a
900 * kludge so that wapi can build a process handle if needed.
901 * As a pseudo handle is returned, we don't need to clean
904 GetCurrentProcess ();
907 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
909 mono_thread_callbacks = callbacks;
913 static void print_tids (gpointer key, gpointer value, gpointer user)
915 g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
921 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
922 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
926 static void wait_for_tids (struct wait_data *wait)
931 g_message(G_GNUC_PRETTY_FUNCTION
932 ": %d threads to wait for in this batch", wait->num);
935 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
936 if(ret==WAIT_FAILED) {
937 /* See the comment in build_wait_tids() */
939 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
945 for(i=0; i<wait->num; i++) {
946 guint32 tid=wait->threads[i]->tid;
948 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
949 /* This thread must have been killed, because
950 * it hasn't cleaned itself up. (It's just
951 * possible that the thread exited before the
952 * parent thread had a chance to store the
953 * handle, and now there is another pointer to
954 * the already-exited thread stored. In this
955 * case, we'll just get two
956 * mono_profiler_thread_end() calls for the
961 g_message (G_GNUC_PRETTY_FUNCTION
962 ": cleaning up after thread %d", tid);
964 thread_cleanup (wait->threads[i]);
969 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
971 struct wait_data *wait=(struct wait_data *)user;
973 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
974 MonoThread *thread=(MonoThread *)value;
976 /* BUG: For now we just ignore background threads, we should abort them
978 if (thread->state & ThreadState_Background)
979 return; /* just leave, ignore */
981 wait->handles[wait->num]=thread->handle;
982 wait->threads[wait->num]=thread;
985 /* Just ignore the rest, we can't do anything with
991 void mono_thread_manage (void)
993 struct wait_data *wait=g_new0 (struct wait_data, 1);
995 /* join each thread that's still running */
997 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1002 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1008 EnterCriticalSection (&threads_mutex);
1010 g_message(G_GNUC_PRETTY_FUNCTION
1011 ":There are %d threads to join",
1012 mono_g_hash_table_size (threads));
1013 mono_g_hash_table_foreach (threads, print_tids, NULL);
1017 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1019 LeaveCriticalSection (&threads_mutex);
1021 /* Something to wait for */
1022 wait_for_tids (wait);
1024 } while(wait->num>0);
1028 mono_g_hash_table_destroy(threads);
1032 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1034 MonoThread *thread=(MonoThread *)value;
1035 guint32 self=GPOINTER_TO_UINT (user);
1037 if(thread->tid!=self) {
1038 /*TerminateThread (thread->handle, -1);*/
1042 void mono_thread_abort_all_other_threads (void)
1044 guint32 self=GetCurrentThreadId ();
1046 EnterCriticalSection (&threads_mutex);
1048 g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1049 mono_g_hash_table_size (threads));
1050 mono_g_hash_table_foreach (threads, print_tids, NULL);
1053 mono_g_hash_table_foreach (threads, terminate_thread,
1054 GUINT_TO_POINTER (self));
1056 LeaveCriticalSection (&threads_mutex);
1059 static int static_data_idx = 0;
1060 static int static_data_offset = 0;
1061 #define NUM_STATIC_DATA_IDX 8
1062 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
1063 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
1067 thread_alloc_static_data (MonoThread *thread, guint32 offset)
1069 guint idx = (offset >> 24) - 1;
1072 if (!thread->static_data) {
1074 thread->static_data = GC_MALLOC (static_data_size [0]);
1076 thread->static_data = g_malloc0 (static_data_size [0]);
1078 thread->static_data [0] = thread->static_data;
1080 for (i = 1; i < idx; ++i) {
1081 if (thread->static_data [i])
1084 thread->static_data [i] = GC_MALLOC (static_data_size [i]);
1086 thread->static_data [i] = g_malloc0 (static_data_size [i]);
1093 * ensure thread static fields already allocated are valid for thread
1094 * This function is called when a thread is created or on thread attach.
1097 thread_adjust_static_data (MonoThread *thread)
1101 EnterCriticalSection (&threads_mutex);
1102 if (static_data_offset || static_data_idx > 0) {
1103 /* get the current allocated size */
1104 offset = static_data_offset | ((static_data_idx + 1) << 24);
1105 thread_alloc_static_data (thread, offset);
1107 LeaveCriticalSection (&threads_mutex);
1111 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
1113 MonoThread *thread = value;
1114 guint32 offset = GPOINTER_TO_UINT (user);
1116 thread_alloc_static_data (thread, offset);
1120 * The offset for a thread static variable is composed of two parts:
1121 * an index in the array of chunks of memory for the thread (thread->static_data)
1122 * and an offset in that chunk of mem. This allows allocating less memory in the
1126 mono_threads_alloc_static_data (guint32 size, guint32 align)
1130 EnterCriticalSection (&threads_mutex);
1132 if (!static_data_idx && !static_data_offset) {
1134 * we use the first chunk of the first allocation also as
1135 * an array for the rest of the data
1137 static_data_offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
1139 static_data_offset += align - 1;
1140 static_data_offset &= ~(align - 1);
1141 if (static_data_offset + size >= static_data_size [static_data_idx]) {
1143 g_assert (size <= static_data_size [static_data_idx]);
1145 * massive unloading and reloading of domains with thread-static
1146 * data may eventually exceed the allocated storage...
1147 * Need to check what the MS runtime does in that case.
1148 * Note that for each appdomain, we need to allocate a separate
1149 * thread data slot for security reasons. We could keep track
1150 * of the slots per-domain and when the domain is unloaded
1151 * out the slots on a sort of free list.
1153 g_assert (static_data_idx < NUM_STATIC_DATA_IDX);
1154 static_data_offset = 0;
1156 offset = static_data_offset | ((static_data_idx + 1) << 24);
1157 static_data_offset += size;
1159 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
1161 LeaveCriticalSection (&threads_mutex);
1166 mono_threads_get_static_data (guint32 offset)
1168 MonoThread *thread = mono_thread_current ();
1169 int idx = offset >> 24;
1171 return ((char*) thread->static_data [idx - 1]) + (offset & 0xffffff);
1174 #ifdef WITH_INCLUDED_LIBGC
1176 static void gc_stop_world (gpointer key, gpointer value, gpointer user)
1178 MonoThread *thread=(MonoThread *)value;
1179 guint32 self=GPOINTER_TO_UINT (user);
1182 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1185 if(thread->tid==self)
1188 SuspendThread (thread->handle);
1191 void mono_gc_stop_world (void)
1193 guint32 self=GetCurrentThreadId ();
1196 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1199 EnterCriticalSection (&threads_mutex);
1201 if (threads != NULL)
1202 mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
1204 LeaveCriticalSection (&threads_mutex);
1207 static void gc_start_world (gpointer key, gpointer value, gpointer user)
1209 MonoThread *thread=(MonoThread *)value;
1210 guint32 self=GPOINTER_TO_UINT (user);
1213 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1216 if(thread->tid==self)
1219 ResumeThread (thread->handle);
1222 void mono_gc_start_world (void)
1224 guint32 self=GetCurrentThreadId ();
1227 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1230 EnterCriticalSection (&threads_mutex);
1232 if (threads != NULL)
1233 mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
1235 LeaveCriticalSection (&threads_mutex);
1238 static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
1240 MonoThread *thread=(MonoThread *)value;
1241 guint32 *selfp=(guint32 *)user, self = *selfp;
1244 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
1247 if(thread->tid==self) {
1249 g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
1251 GC_push_all_stack (selfp, thread->stack_ptr);
1255 #ifdef PLATFORM_WIN32
1256 GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
1258 mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
1262 void mono_gc_push_all_stacks (void)
1264 guint32 self=GetCurrentThreadId ();
1267 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1270 EnterCriticalSection (&threads_mutex);
1272 if (threads != NULL)
1273 mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
1275 LeaveCriticalSection (&threads_mutex);
1278 #endif /* WITH_INCLUDED_LIBGC */