1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
|
/**
* \file
* Low-level threading, posix version
*
* Author:
* Rodrigo Kumpera (kumpera@gmail.com)
*
* (C) 2011 Novell, Inc
*/
#include <config.h>
/* For pthread_main_np, pthread_get_stackaddr_np and pthread_get_stacksize_np */
#if defined (__MACH__)
#define _DARWIN_C_SOURCE 1
#endif
#if defined (__HAIKU__)
#include <os/kernel/OS.h>
#endif
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-coop-semaphore.h>
#include <mono/metadata/gc-internals.h>
#include <mono/utils/mono-threads-debug.h>
#include <errno.h>
#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
#define USE_TKILL_ON_ANDROID 1
#endif
#ifdef USE_TKILL_ON_ANDROID
extern int tkill (pid_t tid, int signal);
#endif
#if defined(_POSIX_VERSION) && !defined (HOST_WASM)
#include <pthread.h>
#include <sys/resource.h>
gboolean
mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
{
pthread_attr_t attr;
pthread_t thread;
gint res;
gsize set_stack_size;
res = pthread_attr_init (&attr);
if (res != 0)
g_error ("%s: pthread_attr_init failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
if (stack_size)
set_stack_size = *stack_size;
else
set_stack_size = 0;
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
if (set_stack_size == 0) {
#if HAVE_VALGRIND_MEMCHECK_H
if (RUNNING_ON_VALGRIND)
set_stack_size = 1 << 20;
else
set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
#else
set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
#endif
}
#ifdef PTHREAD_STACK_MIN
if (set_stack_size < PTHREAD_STACK_MIN)
set_stack_size = PTHREAD_STACK_MIN;
#endif
res = pthread_attr_setstacksize (&attr, set_stack_size);
if (res != 0)
g_error ("%s: pthread_attr_setstacksize failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
#endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
/* Actually start the thread */
res = mono_gc_pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data);
if (res) {
res = pthread_attr_destroy (&attr);
if (res != 0)
g_error ("%s: pthread_attr_destroy failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
return FALSE;
}
if (tid)
*tid = thread;
if (stack_size) {
res = pthread_attr_getstacksize (&attr, stack_size);
if (res != 0)
g_error ("%s: pthread_attr_getstacksize failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
}
res = pthread_attr_destroy (&attr);
if (res != 0)
g_error ("%s: pthread_attr_destroy failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
return TRUE;
}
void
mono_threads_platform_init (void)
{
}
gboolean
mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
{
return FALSE;
}
gboolean
mono_threads_platform_yield (void)
{
return sched_yield () == 0;
}
void
mono_threads_platform_exit (gsize exit_code)
{
pthread_exit ((gpointer) exit_code);
}
int
mono_thread_info_get_system_max_stack_size (void)
{
struct rlimit lim;
/* If getrlimit fails, we don't enforce any limits. */
if (getrlimit (RLIMIT_STACK, &lim))
return INT_MAX;
/* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
if (lim.rlim_max > (rlim_t)INT_MAX)
return INT_MAX;
return (int)lim.rlim_max;
}
int
mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
{
THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
int result;
#ifdef USE_TKILL_ON_ANDROID
int old_errno = errno;
result = tkill (info->native_handle, signum);
if (result < 0) {
result = errno;
errno = old_errno;
}
#elif defined (HAVE_PTHREAD_KILL)
result = pthread_kill (mono_thread_info_get_tid (info), signum);
#else
result = -1;
g_error ("pthread_kill () is not supported by this platform");
#endif
/*
* ESRCH just means the thread is gone; this is usually not fatal.
*
* ENOTSUP can occur if we try to send signals (e.g. for sampling) to Grand
* Central Dispatch threads on Apple platforms. This is kinda bad, but
* since there's really nothing we can do about it, we just ignore it and
* move on.
*
* All other error codes are ill-documented and usually stem from various
* OS-specific idiosyncracies. We want to know about these, so fail loudly.
* One example is EAGAIN on Linux, which indicates a signal queue overflow.
*/
if (result &&
result != ESRCH
#if defined (__MACH__) && defined (ENOTSUP)
&& result != ENOTSUP
#endif
)
g_error ("%s: pthread_kill failed with error %d - potential kernel OOM or signal queue overflow", __func__, result);
return result;
}
MonoNativeThreadId
mono_native_thread_id_get (void)
{
return pthread_self ();
}
gboolean
mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
{
return pthread_equal (id1, id2);
}
/*
* mono_native_thread_create:
*
* Low level thread creation function without any GC wrappers.
*/
gboolean
mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
{
return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
}
size_t
mono_native_thread_get_name (MonoNativeThreadId tid, char *name_out, size_t max_len)
{
#ifdef HAVE_PTHREAD_GETNAME_NP
int error = pthread_getname_np(tid, name_out, max_len);
if (error != 0)
return 0;
return strlen(name_out);
#else
return 0;
#endif
}
void
mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
{
#ifdef __MACH__
/*
* We can't set the thread name for other threads, but we can at least make
* it work for threads that try to change their own name.
*/
if (tid != mono_native_thread_id_get ())
return;
if (!name) {
pthread_setname_np ("");
} else {
char n [63];
strncpy (n, name, sizeof (n) - 1);
n [sizeof (n) - 1] = '\0';
pthread_setname_np (n);
}
#elif defined (__HAIKU__)
thread_id haiku_tid;
haiku_tid = get_pthread_thread_id (tid);
if (!name) {
rename_thread (haiku_tid, "");
} else {
rename_thread (haiku_tid, name);
}
#elif defined (__NetBSD__)
if (!name) {
pthread_setname_np (tid, "%s", (void*)"");
} else {
char n [PTHREAD_MAX_NAMELEN_NP];
strncpy (n, name, sizeof (n) - 1);
n [sizeof (n) - 1] = '\0';
pthread_setname_np (tid, "%s", (void*)n);
}
#elif defined (HAVE_PTHREAD_SETNAME_NP)
if (!name) {
pthread_setname_np (tid, "");
} else {
char n [16];
strncpy (n, name, sizeof (n) - 1);
n [sizeof (n) - 1] = '\0';
pthread_setname_np (tid, n);
}
#endif
}
gboolean
mono_native_thread_join (MonoNativeThreadId tid)
{
void *res;
return !pthread_join (tid, &res);
}
#endif /* defined(_POSIX_VERSION) */
#if defined(USE_POSIX_BACKEND)
gboolean
mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
int sig = interrupt_kernel ? mono_threads_suspend_get_abort_signal () : mono_threads_suspend_get_suspend_signal ();
if (!mono_threads_pthread_kill (info, sig)) {
mono_threads_add_to_pending_operation_set (info);
return TRUE;
}
return FALSE;
}
gboolean
mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
{
return info->suspend_can_continue;
}
/*
This begins async resume. This function must do the following:
- Install an async target if one was requested.
- Notify the target to resume.
*/
gboolean
mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
{
int sig = mono_threads_suspend_get_restart_signal ();
if (!mono_threads_pthread_kill (info, sig)) {
mono_threads_add_to_pending_operation_set (info);
return TRUE;
}
return FALSE;
}
void
mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
{
/* We signal a thread to break it from the current syscall.
* This signal should not be interpreted as a suspend request. */
info->syscall_break_signal = TRUE;
if (mono_threads_pthread_kill (info, mono_threads_suspend_get_abort_signal ()) == 0) {
mono_threads_add_to_pending_operation_set (info);
}
}
void
mono_threads_suspend_register (MonoThreadInfo *info)
{
#if defined (HOST_ANDROID)
info->native_handle = gettid ();
#endif
}
void
mono_threads_suspend_free (MonoThreadInfo *info)
{
}
void
mono_threads_suspend_init (void)
{
}
#endif /* defined(USE_POSIX_BACKEND) */
|