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 static void thread_adjust_static_data (MonoThread *thread);
78 /* Spin lock for InterlockedXXX 64 bit functions */
79 static CRITICAL_SECTION interlocked_mutex;
81 /* handle_store() and handle_remove() manage the array of threads that
82 * still need to be waited for when the main thread exits.
84 static void handle_store(MonoThread *thread)
86 EnterCriticalSection(&threads_mutex);
89 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
94 threads=mono_g_hash_table_new(NULL, NULL);
97 /* We don't need to duplicate thread->handle, because it is
98 * only closed when the thread object is finalized by the GC.
100 mono_g_hash_table_insert(threads, GUINT_TO_POINTER(thread->tid), thread);
101 LeaveCriticalSection(&threads_mutex);
104 static void handle_remove(guint32 tid)
107 g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
110 EnterCriticalSection(&threads_mutex);
112 mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid));
114 LeaveCriticalSection(&threads_mutex);
116 /* Don't close the handle here, wait for the object finalizer
117 * to do it. Otherwise, the following race condition applies:
119 * 1) Thread exits (and handle_remove() closes the handle)
121 * 2) Some other handle is reassigned the same slot
123 * 3) Another thread tries to join the first thread, and
124 * blocks waiting for the reassigned handle to be signalled
125 * (which might never happen). This is possible, because the
126 * thread calling Join() still has a reference to the first
131 static void thread_cleanup (MonoThread *thread)
133 mono_monitor_try_enter ((MonoObject *)thread, INFINITE);
134 thread->state |= ThreadState_Stopped;
135 mono_monitor_exit ((MonoObject *)thread);
137 mono_profiler_thread_end (thread->tid);
138 handle_remove (thread->tid);
139 if (mono_thread_cleanup)
140 mono_thread_cleanup (thread);
143 static guint32 start_wrapper(void *data)
145 struct StartInfo *start_info=(struct StartInfo *)data;
146 guint32 (*start_func)(void *);
149 MonoThread *thread=start_info->obj;
152 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
153 GetCurrentThreadId ());
156 /* We can be sure start_info->obj->tid and
157 * start_info->obj->handle have been set, because the thread
158 * was created suspended, and these values were set before the
164 TlsSetValue (current_object_key, thread);
166 mono_domain_set (start_info->domain);
168 start_func = start_info->func;
169 this = start_info->this;
171 /* This MUST be called before any managed code can be
172 * executed, as it calls the callback function that (for the
173 * jit) sets the lmf marker.
175 mono_thread_new_init (tid, &tid, start_func);
176 thread->stack_ptr = &tid;
179 g_message (G_GNUC_PRETTY_FUNCTION
180 ": (%d,%d) Setting thread stack to %p",
181 GetCurrentThreadId (), getpid (), thread->stack_ptr);
185 g_message (G_GNUC_PRETTY_FUNCTION
186 ": (%d) Setting current_object_key to %p",
187 GetCurrentThreadId (), thread);
190 mono_profiler_thread_start (tid);
192 if(thread->start_notify!=NULL) {
193 /* Let the thread that called Start() know we're
196 ReleaseSemaphore (thread->start_notify, 1, NULL);
201 thread_adjust_static_data (thread);
205 /* If the thread calls ExitThread at all, this remaining code
206 * will not be executed, but the main thread will eventually
207 * call thread_cleanup() on this thread's behalf.
211 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
212 GetCurrentThreadId ());
215 /* Remove the reference to the thread object in the TLS data,
216 * so the thread object can be finalized. This won't be
217 * reached if the thread threw an uncaught exception, so those
218 * thread handles will stay referenced :-( (This is due to
219 * missing support for scanning thread-specific data in the
220 * Boehm GC - the io-layer keeps a GC-visible hash of pointers
223 TlsSetValue (current_object_key, NULL);
225 thread_cleanup (thread);
230 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
232 if (mono_thread_start_cb) {
233 mono_thread_start_cb (tid, stack_start, func);
236 if (mono_thread_callbacks)
237 (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
240 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
243 HANDLE thread_handle;
244 struct StartInfo *start_info;
247 thread=(MonoThread *)mono_object_new (domain,
248 mono_defaults.thread_class);
250 start_info=g_new0 (struct StartInfo, 1);
251 start_info->func = func;
252 start_info->obj = thread;
253 start_info->domain = domain;
254 start_info->this = arg;
256 /* Create suspended, so we can do some housekeeping before the thread
259 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info,
260 CREATE_SUSPENDED, &tid);
262 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
265 g_assert (thread_handle);
267 thread->handle=thread_handle;
270 handle_store(thread);
272 ResumeThread (thread_handle);
276 mono_thread_attach (MonoDomain *domain)
279 HANDLE thread_handle;
282 if ((thread = mono_thread_current ())) {
283 g_warning ("mono_thread_attach called for an already attached thread");
284 if (mono_thread_attach_cb) {
285 mono_thread_attach_cb (tid, &tid);
290 thread = (MonoThread *)mono_object_new (domain,
291 mono_defaults.thread_class);
293 thread_handle = GetCurrentThread ();
294 g_assert (thread_handle);
296 tid=GetCurrentThreadId ();
298 thread->handle=thread_handle;
302 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
306 handle_store(thread);
309 g_message (G_GNUC_PRETTY_FUNCTION
310 ": (%d) Setting current_object_key to %p",
311 GetCurrentThreadId (), thread);
314 TlsSetValue (current_object_key, thread);
315 mono_domain_set (domain);
317 thread_adjust_static_data (thread);
319 if (mono_thread_attach_cb) {
320 mono_thread_attach_cb (tid, &tid);
327 ves_icall_System_Threading_ThreadPool_GetAvailableThreads (int *workerThreads, int *completionPortThreads)
332 *workerThreads = mono_max_worker_threads - mono_worker_threads;
333 *completionPortThreads = 0;
337 ves_icall_System_Threading_ThreadPool_GetMaxThreads (int *workerThreads, int *completionPortThreads)
342 *workerThreads = mono_max_worker_threads;
343 *completionPortThreads = 0;
346 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
349 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
350 guint32 (*start_func)(void *);
351 struct StartInfo *start_info;
359 g_message(G_GNUC_PRETTY_FUNCTION
360 ": Trying to start a new thread: this (%p) start (%p)",
364 im = mono_get_delegate_invoke (start->vtable->klass);
365 if (mono_thread_callbacks)
366 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
368 start_func = mono_compile_method (im);
370 if(start_func==NULL) {
371 g_warning(G_GNUC_PRETTY_FUNCTION
372 ": Can't locate start method!");
375 /* This is freed in start_wrapper */
376 start_info = g_new0 (struct StartInfo, 1);
377 start_info->func = start_func;
378 start_info->this = delegate;
379 start_info->obj = this;
380 start_info->domain = mono_domain_get ();
382 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
383 if(this->start_notify==NULL) {
384 g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
388 thread=CreateThread(NULL, 0, start_wrapper, start_info,
389 CREATE_SUSPENDED, &tid);
391 g_warning(G_GNUC_PRETTY_FUNCTION
392 ": CreateThread error 0x%x", GetLastError());
399 /* Don't call handle_store() here, delay it to Start.
400 * We can't join a thread (trying to will just block
401 * forever) until it actually starts running, so don't
402 * store the handle till then.
406 g_message(G_GNUC_PRETTY_FUNCTION
407 ": Started thread ID %d (handle %p)", tid, thread);
414 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
420 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
424 CloseHandle (thread);
427 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
433 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
434 GetCurrentThreadId (), this, this->tid);
437 /* Only store the handle when the thread is about to be
438 * launched, to avoid the main thread deadlocking while trying
439 * to clean up a thread that will never be signalled.
443 if (mono_thread_callbacks)
444 (* mono_thread_callbacks->start_resume) (this->tid);
446 ResumeThread(thread);
448 if (mono_thread_callbacks)
449 (* mono_thread_callbacks->end_resume) (this->tid);
451 if(this->start_notify!=NULL) {
452 /* Wait for the thread to set up its TLS data etc, so
453 * theres no potential race condition if someone tries
454 * to look up the data believing the thread has
459 g_message(G_GNUC_PRETTY_FUNCTION
460 ": (%d) waiting for thread %p (%d) to start",
461 GetCurrentThreadId (), this, this->tid);
464 WaitForSingleObject (this->start_notify, INFINITE);
465 CloseHandle (this->start_notify);
466 this->start_notify=NULL;
470 g_message(G_GNUC_PRETTY_FUNCTION
471 ": (%d) Done launching thread %p (%d)",
472 GetCurrentThreadId (), this, this->tid);
476 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
481 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
488 ves_icall_System_Threading_Thread_GetDomainID (void)
492 return mono_domain_get()->domain_id;
496 mono_thread_current (void)
502 /* Find the current thread object */
503 thread=TlsGetValue (current_object_key);
506 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
512 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
513 int ms, HANDLE thread)
523 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
527 ret=WaitForSingleObject(thread, ms);
528 if(ret==WAIT_OBJECT_0) {
530 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
537 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
543 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
548 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
551 /* Object location stored here */
552 TlsSetValue(slothash_key, data);
555 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
561 data=TlsGetValue(slothash_key);
564 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
570 /* FIXME: exitContext isnt documented */
571 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
577 MonoObject *waitHandle;
582 numhandles = mono_array_length(mono_handles);
583 handles = g_new0(HANDLE, numhandles);
585 if (wait_handle_os_handle_field == 0) {
586 /* Get the field os_handle which will contain the actual handle */
587 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
588 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
591 for(i = 0; i < numhandles; i++) {
592 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
593 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
600 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
604 if(ret==WAIT_FAILED) {
605 #ifdef THREAD_WAIT_DEBUG
606 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
607 GetCurrentThreadId ());
610 } else if(ret==WAIT_TIMEOUT) {
611 #ifdef THREAD_WAIT_DEBUG
612 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
613 GetCurrentThreadId ());
621 /* FIXME: exitContext isnt documented */
622 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
628 MonoObject *waitHandle;
633 numhandles = mono_array_length(mono_handles);
634 handles = g_new0(HANDLE, numhandles);
636 if (wait_handle_os_handle_field == 0) {
637 /* Get the field os_handle which will contain the actual handle */
638 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
639 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
642 for(i = 0; i < numhandles; i++) {
643 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
644 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
651 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
655 #ifdef THREAD_WAIT_DEBUG
656 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
657 GetCurrentThreadId (), ret);
661 * These need to be here. See MSDN dos on WaitForMultipleObjects.
663 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
664 return ret - WAIT_OBJECT_0;
666 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
667 return ret - WAIT_ABANDONED_0;
674 /* FIXME: exitContext isnt documented */
675 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
681 #ifdef THREAD_WAIT_DEBUG
682 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
683 GetCurrentThreadId (), handle, ms);
690 ret=WaitForSingleObject(handle, ms);
691 if(ret==WAIT_FAILED) {
692 #ifdef THREAD_WAIT_DEBUG
693 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
694 GetCurrentThreadId ());
697 } else if(ret==WAIT_TIMEOUT) {
698 #ifdef THREAD_WAIT_DEBUG
699 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
700 GetCurrentThreadId ());
708 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
711 return(CreateMutex(NULL,owned,name));
714 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
717 ReleaseMutex(handle);
720 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, char *name) {
723 return (CreateEvent(NULL,manual,initial,name));
726 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
729 return (SetEvent(handle));
732 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
735 return (ResetEvent(handle));
739 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
742 CloseHandle (handle);
745 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
749 return InterlockedIncrement (location);
752 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
759 EnterCriticalSection(&interlocked_mutex);
761 lowret = InterlockedIncrement((gint32 *) location);
763 highret = InterlockedIncrement((gint32 *) location + 1);
765 highret = *((gint32 *) location + 1);
767 LeaveCriticalSection(&interlocked_mutex);
769 return (gint64) highret << 32 | (gint64) lowret;
772 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
776 return InterlockedDecrement(location);
779 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
786 EnterCriticalSection(&interlocked_mutex);
788 lowret = InterlockedDecrement((gint32 *) location);
790 highret = InterlockedDecrement((gint32 *) location + 1);
792 highret = *((gint32 *) location + 1);
794 LeaveCriticalSection(&interlocked_mutex);
796 return (gint64) highret << 32 | (gint64) lowret;
799 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
803 return InterlockedExchange(location1, value);
806 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
810 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
813 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
815 IntFloatUnion val, ret;
820 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
825 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
829 return InterlockedCompareExchange(location1, value, comparand);
832 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
836 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
839 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
841 IntFloatUnion val, ret, cmp;
846 cmp.fval = comparand;
847 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
853 mono_thread_get_abort_signal (void)
863 #endif /* __MINGW32__ */
867 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
871 thread->abort_state = state;
872 thread->abort_exc = mono_get_exception_thread_abort ();
875 g_message (G_GNUC_PRETTY_FUNCTION
876 ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
877 thread, thread->tid);
881 g_assert_not_reached ();
883 /* fixme: store the state somewhere */
884 #ifdef PTHREAD_POINTER_ID
885 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
887 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
889 #endif /* __MINGW32__ */
893 ves_icall_System_Threading_Thread_ResetAbort (void)
895 MonoThread *thread = mono_thread_current ();
899 if (!thread->abort_exc) {
900 const char *msg = "Unable to reset abort because no abort was requested";
901 mono_raise_exception (mono_get_exception_thread_state (msg));
903 thread->abort_exc = NULL;
904 thread->abort_state = NULL;
908 void mono_thread_init (MonoThreadStartCB start_cb,
909 MonoThreadAttachCB attach_cb)
911 InitializeCriticalSection(&threads_mutex);
912 InitializeCriticalSection(&interlocked_mutex);
914 current_object_key=TlsAlloc();
916 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
920 mono_thread_start_cb = start_cb;
921 mono_thread_attach_cb = attach_cb;
923 slothash_key=TlsAlloc();
925 /* Get a pseudo handle to the current process. This is just a
926 * kludge so that wapi can build a process handle if needed.
927 * As a pseudo handle is returned, we don't need to clean
930 GetCurrentProcess ();
934 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
936 mono_thread_cleanup = func;
939 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
941 mono_thread_callbacks = callbacks;
945 static void print_tids (gpointer key, gpointer value, gpointer user)
947 g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
953 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
954 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
958 static void wait_for_tids (struct wait_data *wait)
963 g_message(G_GNUC_PRETTY_FUNCTION
964 ": %d threads to wait for in this batch", wait->num);
967 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
968 if(ret==WAIT_FAILED) {
969 /* See the comment in build_wait_tids() */
971 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
977 for(i=0; i<wait->num; i++) {
978 guint32 tid=wait->threads[i]->tid;
980 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
981 /* This thread must have been killed, because
982 * it hasn't cleaned itself up. (It's just
983 * possible that the thread exited before the
984 * parent thread had a chance to store the
985 * handle, and now there is another pointer to
986 * the already-exited thread stored. In this
987 * case, we'll just get two
988 * mono_profiler_thread_end() calls for the
993 g_message (G_GNUC_PRETTY_FUNCTION
994 ": cleaning up after thread %d", tid);
996 thread_cleanup (wait->threads[i]);
1001 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1003 struct wait_data *wait=(struct wait_data *)user;
1005 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1006 MonoThread *thread=(MonoThread *)value;
1008 /* BUG: For now we just ignore background threads, we should abort them
1010 if (thread->state & ThreadState_Background)
1011 return; /* just leave, ignore */
1013 wait->handles[wait->num]=thread->handle;
1014 wait->threads[wait->num]=thread;
1017 /* Just ignore the rest, we can't do anything with
1023 void mono_thread_manage (void)
1025 struct wait_data *wait=g_new0 (struct wait_data, 1);
1027 /* join each thread that's still running */
1029 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1034 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1040 EnterCriticalSection (&threads_mutex);
1042 g_message(G_GNUC_PRETTY_FUNCTION
1043 ":There are %d threads to join",
1044 mono_g_hash_table_size (threads));
1045 mono_g_hash_table_foreach (threads, print_tids, NULL);
1049 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1051 LeaveCriticalSection (&threads_mutex);
1053 /* Something to wait for */
1054 wait_for_tids (wait);
1056 } while(wait->num>0);
1060 mono_g_hash_table_destroy(threads);
1064 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1066 MonoThread *thread=(MonoThread *)value;
1067 guint32 self=GPOINTER_TO_UINT (user);
1069 if(thread->tid!=self) {
1070 /*TerminateThread (thread->handle, -1);*/
1074 void mono_thread_abort_all_other_threads (void)
1076 guint32 self=GetCurrentThreadId ();
1078 EnterCriticalSection (&threads_mutex);
1080 g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1081 mono_g_hash_table_size (threads));
1082 mono_g_hash_table_foreach (threads, print_tids, NULL);
1085 mono_g_hash_table_foreach (threads, terminate_thread,
1086 GUINT_TO_POINTER (self));
1088 LeaveCriticalSection (&threads_mutex);
1091 static int static_data_idx = 0;
1092 static int static_data_offset = 0;
1093 #define NUM_STATIC_DATA_IDX 8
1094 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
1095 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
1099 thread_alloc_static_data (MonoThread *thread, guint32 offset)
1101 guint idx = (offset >> 24) - 1;
1104 if (!thread->static_data) {
1106 thread->static_data = GC_MALLOC (static_data_size [0]);
1108 thread->static_data = g_malloc0 (static_data_size [0]);
1110 thread->static_data [0] = thread->static_data;
1112 for (i = 1; i < idx; ++i) {
1113 if (thread->static_data [i])
1116 thread->static_data [i] = GC_MALLOC (static_data_size [i]);
1118 thread->static_data [i] = g_malloc0 (static_data_size [i]);
1125 * ensure thread static fields already allocated are valid for thread
1126 * This function is called when a thread is created or on thread attach.
1129 thread_adjust_static_data (MonoThread *thread)
1133 EnterCriticalSection (&threads_mutex);
1134 if (static_data_offset || static_data_idx > 0) {
1135 /* get the current allocated size */
1136 offset = static_data_offset | ((static_data_idx + 1) << 24);
1137 thread_alloc_static_data (thread, offset);
1139 LeaveCriticalSection (&threads_mutex);
1143 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
1145 MonoThread *thread = value;
1146 guint32 offset = GPOINTER_TO_UINT (user);
1148 thread_alloc_static_data (thread, offset);
1152 * The offset for a thread static variable is composed of two parts:
1153 * an index in the array of chunks of memory for the thread (thread->static_data)
1154 * and an offset in that chunk of mem. This allows allocating less memory in the
1158 mono_threads_alloc_static_data (guint32 size, guint32 align)
1162 EnterCriticalSection (&threads_mutex);
1164 if (!static_data_idx && !static_data_offset) {
1166 * we use the first chunk of the first allocation also as
1167 * an array for the rest of the data
1169 static_data_offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
1171 static_data_offset += align - 1;
1172 static_data_offset &= ~(align - 1);
1173 if (static_data_offset + size >= static_data_size [static_data_idx]) {
1175 g_assert (size <= static_data_size [static_data_idx]);
1177 * massive unloading and reloading of domains with thread-static
1178 * data may eventually exceed the allocated storage...
1179 * Need to check what the MS runtime does in that case.
1180 * Note that for each appdomain, we need to allocate a separate
1181 * thread data slot for security reasons. We could keep track
1182 * of the slots per-domain and when the domain is unloaded
1183 * out the slots on a sort of free list.
1185 g_assert (static_data_idx < NUM_STATIC_DATA_IDX);
1186 static_data_offset = 0;
1188 offset = static_data_offset | ((static_data_idx + 1) << 24);
1189 static_data_offset += size;
1191 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
1193 LeaveCriticalSection (&threads_mutex);
1198 mono_threads_get_static_data (guint32 offset)
1200 MonoThread *thread = mono_thread_current ();
1201 int idx = offset >> 24;
1203 return ((char*) thread->static_data [idx - 1]) + (offset & 0xffffff);
1206 #ifdef WITH_INCLUDED_LIBGC
1208 static void gc_stop_world (gpointer key, gpointer value, gpointer user)
1210 MonoThread *thread=(MonoThread *)value;
1211 guint32 self=GPOINTER_TO_UINT (user);
1214 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1217 if(thread->tid==self)
1220 SuspendThread (thread->handle);
1223 void mono_gc_stop_world (void)
1225 guint32 self=GetCurrentThreadId ();
1228 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1231 EnterCriticalSection (&threads_mutex);
1233 if (threads != NULL)
1234 mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
1236 LeaveCriticalSection (&threads_mutex);
1239 static void gc_start_world (gpointer key, gpointer value, gpointer user)
1241 MonoThread *thread=(MonoThread *)value;
1242 guint32 self=GPOINTER_TO_UINT (user);
1245 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1248 if(thread->tid==self)
1251 ResumeThread (thread->handle);
1254 void mono_gc_start_world (void)
1256 guint32 self=GetCurrentThreadId ();
1259 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1262 EnterCriticalSection (&threads_mutex);
1264 if (threads != NULL)
1265 mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
1267 LeaveCriticalSection (&threads_mutex);
1270 static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
1272 MonoThread *thread=(MonoThread *)value;
1273 guint32 *selfp=(guint32 *)user, self = *selfp;
1276 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
1279 if(thread->tid==self) {
1281 g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
1283 GC_push_all_stack (selfp, thread->stack_ptr);
1287 #ifdef PLATFORM_WIN32
1288 GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
1290 mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
1294 void mono_gc_push_all_stacks (void)
1296 guint32 self=GetCurrentThreadId ();
1299 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1302 EnterCriticalSection (&threads_mutex);
1304 if (threads != NULL)
1305 mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
1307 LeaveCriticalSection (&threads_mutex);
1310 #endif /* WITH_INCLUDED_LIBGC */