Coverage Report

Created: 2025-07-23 06:50

/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
}