diff options
Diffstat (limited to 'src/systhread.c')
-rw-r--r-- | src/systhread.c | 230 |
1 files changed, 193 insertions, 37 deletions
diff --git a/src/systhread.c b/src/systhread.c index ee89a1ed3ff..08afa7e5836 100644 --- a/src/systhread.c +++ b/src/systhread.c @@ -1,5 +1,5 @@ /* System thread definitions -Copyright (C) 2012-2017 Free Software Foundation, Inc. +Copyright (C) 2012-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -18,12 +18,18 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <config.h> #include <setjmp.h> +#include <stdio.h> +#include <string.h> #include "lisp.h" #ifdef HAVE_NS #include "nsterm.h" #endif +#ifdef HAVE_PTHREAD_SET_NAME_NP +#include <pthread_np.h> +#endif + #ifndef THREADS_ENABLED void @@ -74,11 +80,20 @@ sys_thread_self (void) return 0; } -int -sys_thread_create (sys_thread_t *t, const char *name, - thread_creation_function *func, void *datum) +bool +sys_thread_equal (sys_thread_t t, sys_thread_t u) +{ + return t == u; +} +void +sys_thread_set_name (const char *name) { - return 0; +} + +bool +sys_thread_create (sys_thread_t *t, thread_creation_function *func, void *datum) +{ + return false; } void @@ -90,50 +105,80 @@ sys_thread_yield (void) #include <sched.h> -#ifdef HAVE_SYS_PRCTL_H -#include <sys/prctl.h> -#endif - void sys_mutex_init (sys_mutex_t *mutex) { - pthread_mutex_init (mutex, NULL); + pthread_mutexattr_t *attr_ptr; +#ifdef ENABLE_CHECKING + pthread_mutexattr_t attr; + { + int error = pthread_mutexattr_init (&attr); + eassert (error == 0); + error = pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK); + eassert (error == 0); + } + attr_ptr = &attr; +#else + attr_ptr = NULL; +#endif + int error = pthread_mutex_init (mutex, attr_ptr); + /* We could get ENOMEM. Can't do anything except aborting. */ + if (error != 0) + { + fprintf (stderr, "\npthread_mutex_init failed: %s\n", strerror (error)); + emacs_abort (); + } +#ifdef ENABLE_CHECKING + error = pthread_mutexattr_destroy (&attr); + eassert (error == 0); +#endif } void sys_mutex_lock (sys_mutex_t *mutex) { - pthread_mutex_lock (mutex); + int error = pthread_mutex_lock (mutex); + eassert (error == 0); } void sys_mutex_unlock (sys_mutex_t *mutex) { - pthread_mutex_unlock (mutex); + int error = pthread_mutex_unlock (mutex); + eassert (error == 0); } void sys_cond_init (sys_cond_t *cond) { - pthread_cond_init (cond, NULL); + int error = pthread_cond_init (cond, NULL); + /* We could get ENOMEM. Can't do anything except aborting. */ + if (error != 0) + { + fprintf (stderr, "\npthread_cond_init failed: %s\n", strerror (error)); + emacs_abort (); + } } void sys_cond_wait (sys_cond_t *cond, sys_mutex_t *mutex) { - pthread_cond_wait (cond, mutex); + int error = pthread_cond_wait (cond, mutex); + eassert (error == 0); } void sys_cond_signal (sys_cond_t *cond) { - pthread_cond_signal (cond); + int error = pthread_cond_signal (cond); + eassert (error == 0); } void sys_cond_broadcast (sys_cond_t *cond) { - pthread_cond_broadcast (cond); + int error = pthread_cond_broadcast (cond); + eassert (error == 0); #ifdef HAVE_NS /* Send an app defined event to break out of the NS run loop. It seems that if ns_select is running the NS run loop, this @@ -146,7 +191,8 @@ sys_cond_broadcast (sys_cond_t *cond) void sys_cond_destroy (sys_cond_t *cond) { - pthread_cond_destroy (cond); + int error = pthread_cond_destroy (cond); + eassert (error == 0); } sys_thread_t @@ -155,26 +201,63 @@ sys_thread_self (void) return pthread_self (); } -int -sys_thread_create (sys_thread_t *thread_ptr, const char *name, - thread_creation_function *func, void *arg) +bool +sys_thread_equal (sys_thread_t t, sys_thread_t u) +{ + return pthread_equal (t, u); +} + +void +sys_thread_set_name (const char *name) +{ +#ifdef HAVE_PTHREAD_SETNAME_NP + /* We need to truncate here otherwise pthread_setname_np + fails to set the name. TASK_COMM_LEN is what the length + is called in the Linux kernel headers (Bug#38632). */ +#define TASK_COMM_LEN 16 + char p_name[TASK_COMM_LEN]; + strncpy (p_name, name, TASK_COMM_LEN - 1); + p_name[TASK_COMM_LEN - 1] = '\0'; +# ifdef HAVE_PTHREAD_SETNAME_NP_1ARG + pthread_setname_np (p_name); +# elif defined HAVE_PTHREAD_SETNAME_NP_3ARG + pthread_setname_np (pthread_self (), "%s", p_name); +# else + pthread_setname_np (pthread_self (), p_name); +# endif +#elif HAVE_PTHREAD_SET_NAME_NP + /* The name will automatically be truncated if it exceeds a + system-specific length. */ + pthread_set_name_np (pthread_self (), name); +#endif +} + +bool +sys_thread_create (sys_thread_t *thread_ptr, thread_creation_function *func, + void *arg) { pthread_attr_t attr; - int result = 0; + bool result = false; if (pthread_attr_init (&attr)) - return 0; + return false; - if (!pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED)) + /* Avoid crash on macOS with deeply nested GC (Bug#30364). */ + size_t stack_size; + size_t required_stack_size = sizeof (void *) * 1024 * 1024; + if (pthread_attr_getstacksize (&attr, &stack_size) == 0 + && stack_size < required_stack_size) { - result = pthread_create (thread_ptr, &attr, func, arg) == 0; -#if defined (HAVE_SYS_PRCTL_H) && defined (HAVE_PRCTL) && defined (PR_SET_NAME) - if (result && name != NULL) - prctl (PR_SET_NAME, name); -#endif + if (pthread_attr_setstacksize (&attr, required_stack_size) != 0) + goto out; } - pthread_attr_destroy (&attr); + if (!pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED)) + result = pthread_create (thread_ptr, &attr, func, arg) == 0; + + out: ; + int error = pthread_attr_destroy (&attr); + eassert (error == 0); return result; } @@ -187,7 +270,8 @@ sys_thread_yield (void) #elif defined (WINDOWSNT) -#include <windows.h> +#include <mbctype.h> +#include "w32term.h" /* Cannot include <process.h> because of the local header by the same name, sigh. */ @@ -323,19 +407,91 @@ sys_thread_self (void) return (sys_thread_t) GetCurrentThreadId (); } +bool +sys_thread_equal (sys_thread_t t, sys_thread_t u) +{ + return t == u; +} + +/* Special exception used to communicate with a debugger. The name is + taken from example code shown on MSDN. */ +#define MS_VC_EXCEPTION 0x406d1388UL + +/* Structure used to communicate thread name to a debugger. */ +typedef struct _THREADNAME_INFO +{ + DWORD_PTR type; + LPCSTR name; + DWORD_PTR thread_id; + DWORD_PTR reserved; +} THREADNAME_INFO; + +typedef BOOL (WINAPI *IsDebuggerPresent_Proc) (void); +extern IsDebuggerPresent_Proc is_debugger_present; +extern int (WINAPI *pMultiByteToWideChar)(UINT,DWORD,LPCSTR,int,LPWSTR,int); +typedef HRESULT (WINAPI *SetThreadDescription_Proc) + (HANDLE hThread, PCWSTR lpThreadDescription); +extern SetThreadDescription_Proc set_thread_description; + +/* Set the name of the thread identified by its thread ID. */ +static void +w32_set_thread_name (DWORD thread_id, const char *name) +{ + if (!name || !*name) + return; + + /* Use the new API provided since Windows 10, if available. */ + if (set_thread_description) + { + /* GDB pulls only the first 1024 characters of thread's name. */ + wchar_t name_w[1025]; + /* The thread name is encoded in locale's encoding, but + SetThreadDescription wants a wchar_t string. */ + int codepage = _getmbcp (); + if (!codepage) + codepage = GetACP (); + int cnv_result = pMultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, + name, -1, + name_w, 1025); + if (cnv_result + && set_thread_description (GetCurrentThread (), name_w) == S_OK) + return; + } + /* We can only support this fallback method when Emacs is being + debugged. */ + if (!(is_debugger_present && is_debugger_present ())) + return; + + THREADNAME_INFO tninfo; + + tninfo.type = 0x1000; /* magic constant */ + tninfo.name = name; + tninfo.thread_id = thread_id; + tninfo.reserved = 0; + RaiseException (MS_VC_EXCEPTION, 0, sizeof (tninfo) / sizeof (ULONG_PTR), + (ULONG_PTR *) &tninfo); +} + static thread_creation_function *thread_start_address; +void +sys_thread_set_name (const char *name) +{ + w32_set_thread_name (GetCurrentThreadId (), name); +} + /* _beginthread wants a void function, while we are passed a function - that returns a pointer. So we use a wrapper. */ -static void + that returns a pointer. So we use a wrapper. See the command in + w32term.h about the need for ALIGN_STACK attribute. */ +static void ALIGN_STACK w32_beginthread_wrapper (void *arg) { (void)thread_start_address (arg); } -int -sys_thread_create (sys_thread_t *thread_ptr, const char *name, - thread_creation_function *func, void *arg) +bool +sys_thread_create (sys_thread_t *thread_ptr, thread_creation_function *func, + void *arg) { /* FIXME: Do threads that run Lisp require some minimum amount of stack? Zero here means each thread will get the same amount as @@ -357,7 +513,7 @@ sys_thread_create (sys_thread_t *thread_ptr, const char *name, rule in many places... */ thandle = _beginthread (w32_beginthread_wrapper, stack_size, arg); if (thandle == (uintptr_t)-1L) - return 0; + return false; /* Kludge alert! We use the Windows thread ID, an unsigned 32-bit number, as the sys_thread_t type, because that ID is the only @@ -372,7 +528,7 @@ sys_thread_create (sys_thread_t *thread_ptr, const char *name, Therefore, we return some more or less arbitrary value of the thread ID from this function. */ *thread_ptr = thandle & 0xFFFFFFFF; - return 1; + return true; } void |