#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/mono-error-internals.h>
#include <mono/utils/w32handle.h>
+#include <mono/metadata/w32event.h>
+#include <mono/metadata/w32mutex.h>
#include <mono/metadata/gc-internals.h>
#include <mono/metadata/reflection-internals.h>
#include <signal.h>
#endif
+#if defined(HOST_WIN32)
+#include <objbase.h>
+#endif
+
#if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
#define USE_TKILL_ON_ANDROID 1
#endif
MonoThread *thread;
thread = create_thread_object (domain);
- thread->priority = MONO_THREAD_PRIORITY_NORMAL;
MONO_OBJECT_SETREF (thread, internal_thread, internal);
MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, "thread pinning reference");
}
+ thread->priority = MONO_THREAD_PRIORITY_NORMAL;
+
return thread;
}
+static void
+mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
+{
+ g_assert (internal);
+ g_assert (internal->handle);
+
+ g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
+ g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
+ g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST);
+
+#ifdef HOST_WIN32
+ BOOL res;
+
+ res = SetThreadPriority (internal->handle, priority - 2);
+ if (!res)
+ g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
+#else /* HOST_WIN32 */
+ pthread_t tid;
+ int policy;
+ struct sched_param param;
+ gint res;
+
+ tid = thread_get_tid (internal);
+
+ res = pthread_getschedparam (tid, &policy, ¶m);
+ if (res != 0)
+ g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ int max, min;
+
+ /* Necessary to get valid priority range */
+
+ min = sched_get_priority_min (policy);
+ max = sched_get_priority_max (policy);
+
+ if (max > 0 && min >= 0 && max > min) {
+ double srange, drange, sposition, dposition;
+ srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST;
+ drange = max - min;
+ sposition = priority - MONO_THREAD_PRIORITY_LOWEST;
+ dposition = (sposition / srange) * drange;
+ param.sched_priority = (int)(dposition + min);
+ } else
+#endif
+ {
+ switch (policy) {
+ case SCHED_FIFO:
+ case SCHED_RR:
+ param.sched_priority = 50;
+ break;
+#ifdef SCHED_BATCH
+ case SCHED_BATCH:
+#endif
+ case SCHED_OTHER:
+ param.sched_priority = 0;
+ break;
+ default:
+ g_error ("%s: unknown policy %d", __func__, policy);
+ }
+ }
+
+ res = pthread_setschedparam (tid, policy, ¶m);
+ if (res != 0) {
+ if (res == EPERM) {
+ g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
+ return;
+ }
+ g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
+ }
+#endif /* HOST_WIN32 */
+}
+
static void
mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal);
return 0;
}
+ mono_thread_internal_set_priority (internal, internal->priority);
+
tid = internal->tid;
start_delegate = start_info->start_delegate;
* for the current thead */
mono_thread_cleanup_apartment_state ();
- thread_cleanup (internal);
+ mono_thread_detach_internal (internal);
internal->tid = 0;
- /* Remove the reference to the thread object in the TLS data,
- * so the thread object can be finalized. This won't be
- * reached if the thread threw an uncaught exception, so those
- * thread handles will stay referenced :-( (This is due to
- * missing support for scanning thread-specific data in the
- * Boehm GC - the io-layer keeps a GC-visible hash of pointers
- * to TLS data.)
- */
- SET_CURRENT_OBJECT (NULL);
-
return(0);
}
StartInfo *start_info = NULL;
HANDLE thread_handle;
MonoNativeThreadId tid;
- MonoThreadParm tp;
gboolean ret;
if (start_delegate)
if (stack_size == 0)
stack_size = default_stacksize_for_thread (internal);
- tp.priority = thread->priority;
- tp.stack_size = stack_size;
- tp.creation_flags = 0;
-
- thread_handle = mono_threads_create_thread (start_wrapper, start_info, &tp, &tid);
+ thread_handle = mono_threads_create_thread (start_wrapper, start_info, stack_size, &tid);
if (thread_handle == NULL) {
/* The thread couldn't be created, so set an exception */
mono_error_init (error);
thread = create_thread_object (domain);
- thread->priority = MONO_THREAD_PRIORITY_NORMAL;
internal = create_internal_thread ();
MONO_OBJECT_SETREF (thread, internal_thread, internal);
+ LOCK_THREAD (internal);
+
res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, threadpool_thread, stack_size, error);
return_val_if_nok (error, NULL);
+ UNLOCK_THREAD (internal);
+
return internal;
}
MonoNativeThreadId tid;
gsize stack_ptr;
- if ((internal = mono_thread_internal_current ())) {
+ if (mono_thread_internal_current_is_attached ()) {
if (domain != mono_domain_get ())
mono_domain_set (domain, TRUE);
/* Already attached */
THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
+#ifndef HOST_WIN32
+ mono_w32mutex_abandon ();
+#endif
+
thread_cleanup (thread);
SET_CURRENT_OBJECT (NULL);
return FALSE;
}
+gboolean
+mono_thread_internal_current_is_attached (void)
+{
+ MonoInternalThread *internal;
+
+ internal = GET_CURRENT_OBJECT ();
+ if (!internal)
+ return FALSE;
+
+ return TRUE;
+}
+
void
mono_thread_exit (void)
{
THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
- thread_cleanup (thread);
- SET_CURRENT_OBJECT (NULL);
- mono_domain_unset ();
+ mono_thread_detach_internal (thread);
/* we could add a callback here for embedders to use. */
if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
exit (mono_environment_exitcode_get ());
+
mono_thread_info_exit ();
}
MonoInternalThread *internal = this_obj->internal_thread;
LOCK_THREAD (internal);
- if (internal->handle != NULL)
- priority = mono_thread_info_get_priority ((MonoThreadInfo*) internal->thread_info);
- else
- priority = this_obj->priority;
+ priority = internal->priority;
UNLOCK_THREAD (internal);
+
return priority;
}
MonoInternalThread *internal = this_obj->internal_thread;
LOCK_THREAD (internal);
- this_obj->priority = priority;
+ internal->priority = priority;
if (internal->handle != NULL)
- mono_thread_info_set_priority ((MonoThreadInfo*) internal->thread_info, this_obj->priority);
+ mono_thread_internal_set_priority (internal, priority);
UNLOCK_THREAD (internal);
}
THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
mono_error_set_pending_exception (&error);
- /*
- * These need to be here. See MSDN dos on WaitForMultipleObjects.
- */
- if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
- return map_native_wait_result_to_managed (ret - WAIT_OBJECT_0);
- }
- else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
- return map_native_wait_result_to_managed (ret - WAIT_ABANDONED_0);
- }
- else {
- /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
- return map_native_wait_result_to_managed (ret);
- }
+
+ /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
+ return map_native_wait_result_to_managed (ret);
}
gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
return map_native_wait_result_to_managed (ret);
}
-HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
-{
- HANDLE mutex;
-
- *created = TRUE;
-
- if (name == NULL) {
- mutex = CreateMutex (NULL, owned, NULL);
- } else {
- mutex = CreateMutex (NULL, owned, mono_string_chars (name));
-
- if (GetLastError () == ERROR_ALREADY_EXISTS) {
- *created = FALSE;
- }
- }
-
- return(mutex);
-}
-
-MonoBoolean ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
- return(ReleaseMutex (handle));
-}
-
-HANDLE ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name,
- gint32 rights,
- gint32 *error)
-{
- HANDLE ret;
-
- *error = ERROR_SUCCESS;
-
- ret = OpenMutex (rights, FALSE, mono_string_chars (name));
- if (ret == NULL) {
- *error = GetLastError ();
- }
-
- return(ret);
-}
-
-
-HANDLE ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, gint32 *error)
-{
- HANDLE sem;
-
- if (name == NULL) {
- sem = CreateSemaphore (NULL, initialCount, maximumCount, NULL);
- } else {
- sem = CreateSemaphore (NULL, initialCount, maximumCount,
- mono_string_chars (name));
- }
-
- *error = GetLastError ();
-
- return(sem);
-}
-
-MonoBoolean ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (HANDLE handle, gint32 releaseCount, gint32 *prevcount)
-{
- return ReleaseSemaphore (handle, releaseCount, prevcount);
-}
-
-HANDLE ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error)
-{
- HANDLE sem;
-
- sem = OpenSemaphore (rights, FALSE, mono_string_chars (name));
-
- *error = GetLastError ();
-
- return(sem);
-}
-
-HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name, gint32 *error)
-{
- HANDLE event;
-
- if (name == NULL) {
- event = CreateEvent (NULL, manual, initial, NULL);
- } else {
- event = CreateEvent (NULL, manual, initial,
- mono_string_chars (name));
- }
-
- *error = GetLastError ();
-
- return(event);
-}
-
-gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
- return (SetEvent(handle));
-}
-
-gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
- return (ResetEvent(handle));
-}
-
-void
-ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
- CloseHandle (handle);
-}
-
-HANDLE ves_icall_System_Threading_Events_OpenEvent_internal (MonoString *name,
- gint32 rights,
- gint32 *error)
-{
- HANDLE ret;
-
- ret = OpenEvent (rights, FALSE, mono_string_chars (name));
- if (ret == NULL) {
- *error = GetLastError ();
- } else {
- *error = ERROR_SUCCESS;
- }
-
- return(ret);
-}
-
gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
{
return InterlockedIncrement (location);
* be notified, since it has to rebuild the list of threads to
* wait for.
*/
- SetEvent (background_change_event);
+ mono_w32event_set (background_change_event);
}
}
* be notified, since it has to rebuild the list of threads to
* wait for.
*/
- SetEvent (background_change_event);
+ mono_w32event_set (background_change_event);
}
}
mono_os_mutex_init_recursive(&interlocked_mutex);
mono_os_mutex_init_recursive(&joinable_threads_mutex);
- background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ background_change_event = mono_w32event_create (TRUE, FALSE);
g_assert(background_change_event != NULL);
mono_init_static_data_info (&thread_static_info);
* anything up.
*/
GetCurrentProcess ();
-
- /* Check that the managed and unmanaged layout of MonoInternalThread matches */
- g_assert (MONO_STRUCT_OFFSET (MonoInternalThread, last) == mono_field_get_offset (mono_class_get_field_from_name (mono_defaults.internal_thread_class, "last")));
}
void mono_thread_cleanup (void)
return;
for(i=0; i<wait->num; i++) {
- gsize tid = wait->threads[i]->tid;
+ MonoInternalThread *internal;
- /*
- * On !win32, when the thread handle becomes signalled, it just means the thread has exited user code,
- * it can still run io-layer etc. code. So wait for it to really exit.
- * FIXME: This won't join threads which are not in the joinable_hash yet.
- */
- mono_thread_join ((gpointer)tid);
+ internal = wait->threads [i];
mono_threads_lock ();
- if(mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
- /* This thread must have been killed, because
- * it hasn't cleaned itself up. (It's just
- * possible that the thread exited before the
- * parent thread had a chance to store the
- * handle, and now there is another pointer to
- * the already-exited thread stored. In this
- * case, we'll just get two
- * mono_profiler_thread_end() calls for the
- * same thread.)
- */
-
- mono_threads_unlock ();
- THREAD_DEBUG (g_message ("%s: cleaning up after thread %p (%"G_GSIZE_FORMAT")", __func__, wait->threads[i], tid));
- thread_cleanup (wait->threads[i]);
- } else {
- mono_threads_unlock ();
- }
+ if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
+ g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
+ mono_threads_unlock ();
}
}
return;
if (ret < wait->num) {
- gsize tid = wait->threads[ret]->tid;
+ MonoInternalThread *internal;
+
+ internal = wait->threads [ret];
+
mono_threads_lock ();
- if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
- /* See comment in wait_for_tids about thread cleanup */
- mono_threads_unlock ();
- THREAD_DEBUG (g_message ("%s: cleaning up after thread %"G_GSIZE_FORMAT, __func__, tid));
- thread_cleanup (wait->threads [ret]);
- } else
- mono_threads_unlock ();
+ if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
+ g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
+ mono_threads_unlock ();
}
}
UNLOCK_THREAD (current_thread);
}
- /*since we're killing the thread, unset the current domain.*/
- mono_domain_unset ();
+ /*since we're killing the thread, detach it.*/
+ mono_thread_detach_internal (current_thread);
/* Wake up other threads potentially waiting for us */
mono_thread_info_exit ();
* interrupt the main thread if it is waiting for all
* the other threads.
*/
- SetEvent (background_change_event);
+ mono_w32event_set (background_change_event);
mono_threads_unlock ();
}
THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
mono_g_hash_table_foreach (threads, print_tids, NULL));
- ResetEvent (background_change_event);
+ mono_w32event_reset (background_change_event);
wait->num=0;
/*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
#if 0
/* This no longer works with remote unwinding */
g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
- mono_thread_info_describe (info, text);
+ mono_thread_internal_describe (thread, text);
g_string_append (text, "\n");
#endif
UNLOCK_THREAD (thread);
return TRUE;
-}
\ No newline at end of file
+}
+
+void
+mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
+{
+ g_string_append_printf (text, ", thread handle : %p", internal->handle);
+
+ if (internal->thread_info) {
+ g_string_append (text, ", state : ");
+ mono_thread_info_describe_interrupt_token ((MonoThreadInfo*) internal->thread_info, text);
+ }
+
+ if (internal->owned_mutexes) {
+ int i;
+
+ g_string_append (text, ", owns : [");
+ for (i = 0; i < internal->owned_mutexes->len; i++)
+ g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
+ g_string_append (text, "]");
+ }
+}
+
+gboolean
+mono_thread_internal_is_current (MonoInternalThread *internal)
+{
+ g_assert (internal);
+ return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));
+}