diff options
| author | Eli Zaretskii | 2019-12-20 20:59:07 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2019-12-20 20:59:07 +0200 |
| commit | 0e19b5d757d88eedd23709a4ea40aa1512a1ff21 (patch) | |
| tree | 5ab479668337f979c29e41e9254fa609d463391c /src | |
| parent | 85a60da92d4db5c87ecfa152501d246425550fc3 (diff) | |
| download | emacs-0e19b5d757d88eedd23709a4ea40aa1512a1ff21.tar.gz emacs-0e19b5d757d88eedd23709a4ea40aa1512a1ff21.zip | |
Support setting OS names of threads on MS-Windows
* src/w32fns.c (setup_w32_kbdhook): Don't initialize
is_debugger_present here...
(globals_of_w32fns): ...initialize it here. Also initialize
the new global variable set_thread_description.
* src/systhread.c: [WINDOWSNT] Include mbctype.h
(w32_set_thread_name): New function.
(MS_VC_EXCEPTION): New macro.
(THREADNAME_INFO, IsDebuggerPresent_Proc)
(SetThreadDescription_Proc): New typedefs.
(w32_beginthread_wrapper): Call w32_set_thread_name to set the
name of the new thread.
* src/thread.h (struct thread_state): New member thread_name.
* src/thread.c (Fmake_thread): Set the thread_name field of
the new thread object.
(run_thread): Free the thread_name member after the thread
exits.
Diffstat (limited to 'src')
| -rw-r--r-- | src/systhread.c | 70 | ||||
| -rw-r--r-- | src/thread.c | 6 | ||||
| -rw-r--r-- | src/thread.h | 4 | ||||
| -rw-r--r-- | src/w32fns.c | 16 |
4 files changed, 91 insertions, 5 deletions
diff --git a/src/systhread.c b/src/systhread.c index 6f4de536fba..baa30751c79 100644 --- a/src/systhread.c +++ b/src/systhread.c | |||
| @@ -248,7 +248,8 @@ sys_thread_yield (void) | |||
| 248 | 248 | ||
| 249 | #elif defined (WINDOWSNT) | 249 | #elif defined (WINDOWSNT) |
| 250 | 250 | ||
| 251 | #include <w32term.h> | 251 | #include <mbctype.h> |
| 252 | #include "w32term.h" | ||
| 252 | 253 | ||
| 253 | /* Cannot include <process.h> because of the local header by the same | 254 | /* Cannot include <process.h> because of the local header by the same |
| 254 | name, sigh. */ | 255 | name, sigh. */ |
| @@ -390,6 +391,65 @@ sys_thread_equal (sys_thread_t t, sys_thread_t u) | |||
| 390 | return t == u; | 391 | return t == u; |
| 391 | } | 392 | } |
| 392 | 393 | ||
| 394 | /* Special exception used to communicate with a debugger. The name is | ||
| 395 | taken from example code shown on MSDN. */ | ||
| 396 | #define MS_VC_EXCEPTION 0x406d1388UL | ||
| 397 | |||
| 398 | /* Structure used to communicate thread name to a debugger. */ | ||
| 399 | typedef struct _THREADNAME_INFO | ||
| 400 | { | ||
| 401 | DWORD_PTR type; | ||
| 402 | LPCSTR name; | ||
| 403 | DWORD_PTR thread_id; | ||
| 404 | DWORD_PTR reserved; | ||
| 405 | } THREADNAME_INFO; | ||
| 406 | |||
| 407 | typedef BOOL (WINAPI *IsDebuggerPresent_Proc) (void); | ||
| 408 | extern IsDebuggerPresent_Proc is_debugger_present; | ||
| 409 | extern int (WINAPI *pMultiByteToWideChar)(UINT,DWORD,LPCSTR,int,LPWSTR,int); | ||
| 410 | typedef HRESULT (WINAPI *SetThreadDescription_Proc) | ||
| 411 | (HANDLE hThread, PCWSTR lpThreadDescription); | ||
| 412 | extern SetThreadDescription_Proc set_thread_description; | ||
| 413 | |||
| 414 | /* Set the name of the thread identified by its thread ID. */ | ||
| 415 | static void | ||
| 416 | w32_set_thread_name (DWORD thread_id, const char *name) | ||
| 417 | { | ||
| 418 | if (!name || !*name) | ||
| 419 | return; | ||
| 420 | |||
| 421 | /* Use the new API provided since Windows 10, if available. */ | ||
| 422 | if (set_thread_description) | ||
| 423 | { | ||
| 424 | /* GDB pulls only the first 1024 characters of thread's name. */ | ||
| 425 | wchar_t name_w[1025]; | ||
| 426 | /* The thread name is encoded in locale's encoding, but | ||
| 427 | SetThreadDescription wants a wchar_t string. */ | ||
| 428 | int codepage = _getmbcp (); | ||
| 429 | if (!codepage) | ||
| 430 | codepage = GetACP (); | ||
| 431 | int cnv_result = pMultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, | ||
| 432 | name, -1, | ||
| 433 | name_w, 1025); | ||
| 434 | if (cnv_result | ||
| 435 | && set_thread_description (GetCurrentThread (), name_w) == S_OK) | ||
| 436 | return; | ||
| 437 | } | ||
| 438 | /* We can only support this fallback method when Emacs is being | ||
| 439 | debugged. */ | ||
| 440 | if (!(is_debugger_present && is_debugger_present ())) | ||
| 441 | return; | ||
| 442 | |||
| 443 | THREADNAME_INFO tninfo; | ||
| 444 | |||
| 445 | tninfo.type = 0x1000; /* magic constant */ | ||
| 446 | tninfo.name = name; | ||
| 447 | tninfo.thread_id = thread_id; | ||
| 448 | tninfo.reserved = 0; | ||
| 449 | RaiseException (MS_VC_EXCEPTION, 0, sizeof (tninfo) / sizeof (ULONG_PTR), | ||
| 450 | (ULONG_PTR *) &tninfo); | ||
| 451 | } | ||
| 452 | |||
| 393 | static thread_creation_function *thread_start_address; | 453 | static thread_creation_function *thread_start_address; |
| 394 | 454 | ||
| 395 | /* _beginthread wants a void function, while we are passed a function | 455 | /* _beginthread wants a void function, while we are passed a function |
| @@ -398,6 +458,14 @@ static thread_creation_function *thread_start_address; | |||
| 398 | static void ALIGN_STACK | 458 | static void ALIGN_STACK |
| 399 | w32_beginthread_wrapper (void *arg) | 459 | w32_beginthread_wrapper (void *arg) |
| 400 | { | 460 | { |
| 461 | /* FIXME: This isn't very clean: systhread.c is not supposed to know | ||
| 462 | that ARG is a pointer to a thread_state object, or be familiar | ||
| 463 | with thread_state object's structure in general. */ | ||
| 464 | struct thread_state *this_thread = arg; | ||
| 465 | |||
| 466 | if (this_thread->thread_name) | ||
| 467 | w32_set_thread_name (GetCurrentThreadId (), this_thread->thread_name); | ||
| 468 | |||
| 401 | (void)thread_start_address (arg); | 469 | (void)thread_start_address (arg); |
| 402 | } | 470 | } |
| 403 | 471 | ||
diff --git a/src/thread.c b/src/thread.c index e2deadd7a83..42c2bf52d28 100644 --- a/src/thread.c +++ b/src/thread.c | |||
| @@ -756,6 +756,8 @@ run_thread (void *state) | |||
| 756 | } | 756 | } |
| 757 | } | 757 | } |
| 758 | 758 | ||
| 759 | xfree (self->thread_name); | ||
| 760 | |||
| 759 | current_thread = NULL; | 761 | current_thread = NULL; |
| 760 | sys_cond_broadcast (&self->thread_condvar); | 762 | sys_cond_broadcast (&self->thread_condvar); |
| 761 | 763 | ||
| @@ -825,6 +827,10 @@ If NAME is given, it must be a string; it names the new thread. */) | |||
| 825 | all_threads = new_thread; | 827 | all_threads = new_thread; |
| 826 | 828 | ||
| 827 | char const *c_name = !NILP (name) ? SSDATA (ENCODE_UTF_8 (name)) : NULL; | 829 | char const *c_name = !NILP (name) ? SSDATA (ENCODE_UTF_8 (name)) : NULL; |
| 830 | if (c_name) | ||
| 831 | new_thread->thread_name = xstrdup (c_name); | ||
| 832 | else | ||
| 833 | new_thread->thread_name = NULL; | ||
| 828 | sys_thread_t thr; | 834 | sys_thread_t thr; |
| 829 | if (! sys_thread_create (&thr, c_name, run_thread, new_thread)) | 835 | if (! sys_thread_create (&thr, c_name, run_thread, new_thread)) |
| 830 | { | 836 | { |
diff --git a/src/thread.h b/src/thread.h index 498b9909c9f..2b85f0893e7 100644 --- a/src/thread.h +++ b/src/thread.h | |||
| @@ -169,6 +169,10 @@ struct thread_state | |||
| 169 | interrupter should broadcast to this condition. */ | 169 | interrupter should broadcast to this condition. */ |
| 170 | sys_cond_t *wait_condvar; | 170 | sys_cond_t *wait_condvar; |
| 171 | 171 | ||
| 172 | /* Thread's name in the locale encoding. Actually used only on | ||
| 173 | WINDOWSNT. */ | ||
| 174 | char *thread_name; | ||
| 175 | |||
| 172 | /* This thread might have released the global lock. If so, this is | 176 | /* This thread might have released the global lock. If so, this is |
| 173 | non-zero. When a thread runs outside thread_select with this | 177 | non-zero. When a thread runs outside thread_select with this |
| 174 | flag non-zero, it means it has been interrupted by SIGINT while | 178 | flag non-zero, it means it has been interrupted by SIGINT while |
diff --git a/src/w32fns.c b/src/w32fns.c index 4ef075f715b..bf2a7a3e54e 100644 --- a/src/w32fns.c +++ b/src/w32fns.c | |||
| @@ -178,6 +178,10 @@ typedef BOOL (WINAPI * EnumDisplayMonitors_Proc) | |||
| 178 | typedef BOOL (WINAPI * GetTitleBarInfo_Proc) | 178 | typedef BOOL (WINAPI * GetTitleBarInfo_Proc) |
| 179 | (IN HWND hwnd, OUT TITLEBAR_INFO* info); | 179 | (IN HWND hwnd, OUT TITLEBAR_INFO* info); |
| 180 | 180 | ||
| 181 | typedef BOOL (WINAPI *IsDebuggerPresent_Proc) (void); | ||
| 182 | typedef HRESULT (WINAPI *SetThreadDescription_Proc) | ||
| 183 | (HANDLE hThread, PCWSTR lpThreadDescription); | ||
| 184 | |||
| 181 | TrackMouseEvent_Proc track_mouse_event_fn = NULL; | 185 | TrackMouseEvent_Proc track_mouse_event_fn = NULL; |
| 182 | ImmGetCompositionString_Proc get_composition_string_fn = NULL; | 186 | ImmGetCompositionString_Proc get_composition_string_fn = NULL; |
| 183 | ImmGetContext_Proc get_ime_context_fn = NULL; | 187 | ImmGetContext_Proc get_ime_context_fn = NULL; |
| @@ -188,6 +192,8 @@ GetMonitorInfo_Proc get_monitor_info_fn = NULL; | |||
| 188 | MonitorFromWindow_Proc monitor_from_window_fn = NULL; | 192 | MonitorFromWindow_Proc monitor_from_window_fn = NULL; |
| 189 | EnumDisplayMonitors_Proc enum_display_monitors_fn = NULL; | 193 | EnumDisplayMonitors_Proc enum_display_monitors_fn = NULL; |
| 190 | GetTitleBarInfo_Proc get_title_bar_info_fn = NULL; | 194 | GetTitleBarInfo_Proc get_title_bar_info_fn = NULL; |
| 195 | IsDebuggerPresent_Proc is_debugger_present = NULL; | ||
| 196 | SetThreadDescription_Proc set_thread_description = NULL; | ||
| 191 | 197 | ||
| 192 | extern AppendMenuW_Proc unicode_append_menu; | 198 | extern AppendMenuW_Proc unicode_append_menu; |
| 193 | 199 | ||
| @@ -284,8 +290,6 @@ static struct | |||
| 284 | } kbdhook; | 290 | } kbdhook; |
| 285 | typedef HWND (WINAPI *GetConsoleWindow_Proc) (void); | 291 | typedef HWND (WINAPI *GetConsoleWindow_Proc) (void); |
| 286 | 292 | ||
| 287 | typedef BOOL (WINAPI *IsDebuggerPresent_Proc) (void); | ||
| 288 | |||
| 289 | /* stdin, from w32console.c */ | 293 | /* stdin, from w32console.c */ |
| 290 | extern HANDLE keyboard_handle; | 294 | extern HANDLE keyboard_handle; |
| 291 | 295 | ||
| @@ -2737,8 +2741,6 @@ setup_w32_kbdhook (void) | |||
| 2737 | hook if the process is being debugged. */ | 2741 | hook if the process is being debugged. */ |
| 2738 | if (w32_kbdhook_active) | 2742 | if (w32_kbdhook_active) |
| 2739 | { | 2743 | { |
| 2740 | IsDebuggerPresent_Proc is_debugger_present = (IsDebuggerPresent_Proc) | ||
| 2741 | get_proc_addr (GetModuleHandle ("kernel32.dll"), "IsDebuggerPresent"); | ||
| 2742 | if (is_debugger_present && is_debugger_present ()) | 2744 | if (is_debugger_present && is_debugger_present ()) |
| 2743 | return; | 2745 | return; |
| 2744 | } | 2746 | } |
| @@ -11031,6 +11033,12 @@ globals_of_w32fns (void) | |||
| 11031 | get_proc_addr (imm32_lib, "ImmSetCompositionWindow"); | 11033 | get_proc_addr (imm32_lib, "ImmSetCompositionWindow"); |
| 11032 | } | 11034 | } |
| 11033 | 11035 | ||
| 11036 | HMODULE hm_kernel32 = GetModuleHandle ("kernel32.dll"); | ||
| 11037 | is_debugger_present = (IsDebuggerPresent_Proc) | ||
| 11038 | get_proc_addr (hm_kernel32, "IsDebuggerPresent"); | ||
| 11039 | set_thread_description = (SetThreadDescription_Proc) | ||
| 11040 | get_proc_addr (hm_kernel32, "SetThreadDescription"); | ||
| 11041 | |||
| 11034 | except_code = 0; | 11042 | except_code = 0; |
| 11035 | except_addr = 0; | 11043 | except_addr = 0; |
| 11036 | #ifndef CYGWIN | 11044 | #ifndef CYGWIN |