/src/mysql-server/mysys/my_thread.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2000, 2025, Oracle and/or its affiliates. |
2 | | |
3 | | This program is free software; you can redistribute it and/or modify |
4 | | it under the terms of the GNU General Public License, version 2.0, |
5 | | as published by the Free Software Foundation. |
6 | | |
7 | | This program is designed to work with certain software (including |
8 | | but not limited to OpenSSL) that is licensed under separate terms, |
9 | | as designated in a particular file or component or in included license |
10 | | documentation. The authors of MySQL hereby grant you an additional |
11 | | permission to link the program and your derivative works with the |
12 | | separately licensed software that they have either included with |
13 | | the program or referenced in the documentation. |
14 | | |
15 | | Without limiting anything contained in the foregoing, this file, |
16 | | which is part of C Driver for MySQL (Connector/C), is also subject to the |
17 | | Universal FOSS Exception, version 1.0, a copy of which can be found at |
18 | | http://oss.oracle.com/licenses/universal-foss-exception. |
19 | | |
20 | | This program is distributed in the hope that it will be useful, |
21 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | | GNU General Public License, version 2.0, for more details. |
24 | | |
25 | | You should have received a copy of the GNU General Public License |
26 | | along with this program; if not, write to the Free Software |
27 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
28 | | |
29 | | /** |
30 | | @file mysys/my_thread.cc |
31 | | */ |
32 | | |
33 | | #include "my_config.h" |
34 | | |
35 | | #ifdef HAVE_PTHREAD_SETNAME_NP_LINUX |
36 | | #include <cstring> |
37 | | #ifndef _GNU_SOURCE |
38 | | #define _GNU_SOURCE |
39 | | #endif |
40 | | #include <pthread.h> |
41 | | #endif /* HAVE_PTHREAD_SETNAME_NP_LINUX */ |
42 | | |
43 | | #ifdef HAVE_PTHREAD_SETNAME_NP_MACOS |
44 | | #include <pthread.h> |
45 | | #endif /* HAVE_PTHREAD_SETNAME_NP_MACOS */ |
46 | | |
47 | | #ifdef _WIN32 |
48 | | #include <windows.h> |
49 | | |
50 | | #include <processthreadsapi.h> |
51 | | |
52 | | #include <stringapiset.h> |
53 | | #endif /* _WIN32 */ |
54 | | #include "my_thread.h" |
55 | | #include "mysql/components/services/bits/my_thread_bits.h" |
56 | | |
57 | | #ifdef _WIN32 |
58 | | #include <errno.h> |
59 | | #include <process.h> |
60 | | #include <signal.h> |
61 | | #include "my_sys.h" /* my_osmaperr */ |
62 | | |
63 | | struct thread_start_parameter { |
64 | | my_start_routine func; |
65 | | void *arg; |
66 | | }; |
67 | | |
68 | | static unsigned int __stdcall win_thread_start(void *p) { |
69 | | struct thread_start_parameter *par = (struct thread_start_parameter *)p; |
70 | | my_start_routine func = par->func; |
71 | | void *arg = par->arg; |
72 | | free(p); |
73 | | (*func)(arg); |
74 | | return 0; |
75 | | } |
76 | | #endif |
77 | | |
78 | | int my_thread_create(my_thread_handle *thread, const my_thread_attr_t *attr, |
79 | 0 | my_start_routine func, void *arg) { |
80 | 0 | #ifndef _WIN32 |
81 | 0 | return pthread_create(&thread->thread, attr, func, arg); |
82 | | #else |
83 | | struct thread_start_parameter *par; |
84 | | unsigned int stack_size; |
85 | | |
86 | | par = (struct thread_start_parameter *)malloc(sizeof(*par)); |
87 | | if (!par) goto error_return; |
88 | | |
89 | | par->func = func; |
90 | | par->arg = arg; |
91 | | stack_size = attr ? attr->dwStackSize : 0; |
92 | | |
93 | | thread->handle = |
94 | | (HANDLE)_beginthreadex(nullptr, stack_size, win_thread_start, par, 0, |
95 | | (unsigned int *)&thread->thread); |
96 | | |
97 | | if (thread->handle) { |
98 | | /* Note that JOINABLE is default, so attr == nullptr => JOINABLE. */ |
99 | | if (attr && attr->detachstate == MY_THREAD_CREATE_DETACHED) { |
100 | | /* |
101 | | Close handles for detached threads right away to avoid leaking |
102 | | handles. For joinable threads we need the handle during |
103 | | my_thread_join. It will be closed there. |
104 | | */ |
105 | | CloseHandle(thread->handle); |
106 | | thread->handle = nullptr; |
107 | | } |
108 | | return 0; |
109 | | } |
110 | | |
111 | | my_osmaperr(GetLastError()); |
112 | | free(par); |
113 | | |
114 | | error_return: |
115 | | thread->thread = 0; |
116 | | thread->handle = nullptr; |
117 | | return 1; |
118 | | #endif |
119 | 0 | } |
120 | | |
121 | 0 | int my_thread_join(my_thread_handle *thread, void **value_ptr) { |
122 | 0 | #ifndef _WIN32 |
123 | 0 | return pthread_join(thread->thread, value_ptr); |
124 | | #else |
125 | | (void)value_ptr; // maybe unused |
126 | | |
127 | | DWORD ret; |
128 | | int result = 0; |
129 | | ret = WaitForSingleObject(thread->handle, INFINITE); |
130 | | if (ret != WAIT_OBJECT_0) { |
131 | | my_osmaperr(GetLastError()); |
132 | | result = 1; |
133 | | } |
134 | | if (thread->handle) CloseHandle(thread->handle); |
135 | | thread->thread = 0; |
136 | | thread->handle = nullptr; |
137 | | return result; |
138 | | #endif |
139 | 0 | } |
140 | | |
141 | 0 | int my_thread_cancel(my_thread_handle *thread) { |
142 | 0 | #ifndef _WIN32 |
143 | 0 | return pthread_cancel(thread->thread); |
144 | | #else |
145 | | bool ok = false; |
146 | | |
147 | | if (thread->handle) { |
148 | | ok = TerminateThread(thread->handle, 0); |
149 | | CloseHandle(thread->handle); |
150 | | } |
151 | | if (ok) return 0; |
152 | | |
153 | | errno = EINVAL; |
154 | | return -1; |
155 | | #endif |
156 | 0 | } |
157 | | |
158 | | // _endthreadex(_ReturnCode) is not tagged with noreturn. |
159 | | #ifdef _WIN32 |
160 | | MY_COMPILER_DIAGNOSTIC_PUSH() |
161 | | MY_COMPILER_CLANG_DIAGNOSTIC_IGNORE("-Winvalid-noreturn") |
162 | | #endif |
163 | 0 | void my_thread_exit(void *value_ptr [[maybe_unused]]) { |
164 | 0 | #ifndef _WIN32 |
165 | 0 | pthread_exit(value_ptr); |
166 | | #else |
167 | | _endthreadex(0); |
168 | | #endif |
169 | 0 | } |
170 | | #ifdef _WIN32 |
171 | | MY_COMPILER_DIAGNOSTIC_POP() |
172 | | #endif |
173 | | |
174 | | /** |
175 | | Maximum name length used for my_thread_self_setname(), |
176 | | including the terminating NUL character. |
177 | | Linux pthread_setname_np(3) is restricted to 15+1 chars, |
178 | | so we use the same limit on all platforms. |
179 | | */ |
180 | | #define SETNAME_MAX_LENGTH 16 |
181 | | |
182 | | #ifdef _WIN32 |
183 | | template <class TMethod> |
184 | | class Win32_library_procedure { |
185 | | public: |
186 | | Win32_library_procedure(std::string module, std::string func_name) |
187 | | : m_module(LoadLibrary(module.c_str())), m_func(nullptr) { |
188 | | if (m_module != nullptr) { |
189 | | m_func = reinterpret_cast<TMethod *>( |
190 | | GetProcAddress(m_module, func_name.c_str())); |
191 | | } |
192 | | } |
193 | | ~Win32_library_procedure() { |
194 | | if (m_module != nullptr) { |
195 | | FreeLibrary(m_module); |
196 | | } |
197 | | } |
198 | | bool is_valid() { return m_func != nullptr; } |
199 | | template <typename... TArgs> |
200 | | auto operator()(TArgs... args) { |
201 | | return m_func(std::forward<TArgs>(args)...); |
202 | | } |
203 | | |
204 | | private: |
205 | | HMODULE m_module; |
206 | | TMethod *m_func; |
207 | | }; |
208 | | |
209 | | static Win32_library_procedure<decltype(SetThreadDescription)> |
210 | | set_thread_name_proc("kernel32.dll", "SetThreadDescription"); |
211 | | #endif |
212 | | |
213 | 0 | void my_thread_self_setname(const char *name [[maybe_unused]]) { |
214 | 0 | #ifdef HAVE_PTHREAD_SETNAME_NP_LINUX |
215 | | /* |
216 | | GNU extension, see pthread_setname_np(3) |
217 | | */ |
218 | 0 | char truncated_name[SETNAME_MAX_LENGTH]; |
219 | 0 | strncpy(truncated_name, name, sizeof(truncated_name) - 1); |
220 | 0 | truncated_name[sizeof(truncated_name) - 1] = '\0'; |
221 | 0 | pthread_setname_np(pthread_self(), truncated_name); |
222 | | #elif defined(HAVE_PTHREAD_SETNAME_NP_MACOS) |
223 | | pthread_setname_np(name); |
224 | | #elif _WIN32 |
225 | | /* Check if we can use the new Windows 10 API. */ |
226 | | if (set_thread_name_proc.is_valid()) { |
227 | | wchar_t w_name[SETNAME_MAX_LENGTH]; |
228 | | int size; |
229 | | |
230 | | size = |
231 | | MultiByteToWideChar(CP_UTF8, 0, name, -1, w_name, SETNAME_MAX_LENGTH); |
232 | | if (size > 0 && size <= SETNAME_MAX_LENGTH) { |
233 | | /* Make sure w_name is NUL terminated when truncated. */ |
234 | | w_name[SETNAME_MAX_LENGTH - 1] = 0; |
235 | | set_thread_name_proc(GetCurrentThread(), w_name); |
236 | | } |
237 | | } |
238 | | |
239 | | /* According to Microsoft documentation there is a "secret handshake" between |
240 | | debuggee & debugger using the special values used below. We use it always in |
241 | | case there is a debugger attached, even if the new Win10 API for thread names |
242 | | is available. */ |
243 | | constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; |
244 | | #pragma pack(push, 8) |
245 | | struct THREADNAME_INFO { |
246 | | DWORD dwType; |
247 | | LPCSTR szName; |
248 | | DWORD dwThreadID; |
249 | | DWORD dwFlags; |
250 | | }; |
251 | | #pragma pack(pop) |
252 | | |
253 | | THREADNAME_INFO info; |
254 | | info.dwType = 0x1000; |
255 | | info.szName = name; |
256 | | info.dwThreadID = GetCurrentThreadId(); |
257 | | info.dwFlags = 0; |
258 | | |
259 | | #pragma warning(push) |
260 | | #pragma warning(disable : 6320 6322) |
261 | | __try { |
262 | | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), |
263 | | (ULONG_PTR *)&info); |
264 | | } __except (EXCEPTION_EXECUTE_HANDLER) { |
265 | | } |
266 | | #pragma warning(pop) |
267 | | |
268 | | #else |
269 | | /* Do nothing for this platform. */ |
270 | | return; |
271 | | #endif |
272 | 0 | } |