Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include <stdint.h>
4 : : #include <stdio.h>
5 : : #include <stdlib.h>
6 : : #include <string.h>
7 : : #include <inttypes.h>
8 : :
9 : : #include "julia.h"
10 : : #include "julia_internal.h"
11 : : #include "julia_assert.h"
12 : :
13 : : // Ref https://www.uclibc.org/docs/tls.pdf
14 : : // For variant 1 JL_ELF_TLS_INIT_SIZE is the size of the thread control block (TCB)
15 : : // For variant 2 JL_ELF_TLS_INIT_SIZE is 0
16 : : #ifdef _OS_LINUX_
17 : : # if defined(_CPU_X86_64_) || defined(_CPU_X86_)
18 : : # define JL_ELF_TLS_VARIANT 2
19 : : # define JL_ELF_TLS_INIT_SIZE 0
20 : : # elif defined(_CPU_AARCH64_)
21 : : # define JL_ELF_TLS_VARIANT 1
22 : : # define JL_ELF_TLS_INIT_SIZE 16
23 : : # elif defined(__ARM_ARCH) && __ARM_ARCH >= 7
24 : : # define JL_ELF_TLS_VARIANT 1
25 : : # define JL_ELF_TLS_INIT_SIZE 8
26 : : # endif
27 : : #endif
28 : :
29 : : #ifdef JL_ELF_TLS_VARIANT
30 : : # include <link.h>
31 : : #endif
32 : :
33 : : #ifdef __cplusplus
34 : : extern "C" {
35 : : #endif
36 : :
37 : : #include "threading.h"
38 : :
39 : : JL_DLLEXPORT _Atomic(uint8_t) jl_measure_compile_time_enabled = 0;
40 : : JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_compile_time = 0;
41 : : JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_recompile_time = 0;
42 : :
43 : 138574 : JL_DLLEXPORT void *jl_get_ptls_states(void)
44 : : {
45 : : // mostly deprecated: use current_task instead
46 : 138574 : return jl_current_task->ptls;
47 : : }
48 : :
49 : : #if !defined(_OS_WINDOWS_)
50 : : static pthread_key_t jl_safe_restore_key;
51 : :
52 : 15 : __attribute__((constructor)) void _jl_init_safe_restore(void)
53 : : {
54 : 15 : pthread_key_create(&jl_safe_restore_key, NULL);
55 : 15 : }
56 : :
57 : 30250 : JL_DLLEXPORT jl_jmp_buf *jl_get_safe_restore(void)
58 : : {
59 : 30250 : return (jl_jmp_buf*)pthread_getspecific(jl_safe_restore_key);
60 : : }
61 : :
62 : 18360 : JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *sr)
63 : : {
64 : 18360 : pthread_setspecific(jl_safe_restore_key, (void*)sr);
65 : 18360 : }
66 : : #endif
67 : :
68 : :
69 : : // The tls_states buffer:
70 : : //
71 : : // On platforms that do not use ELF (i.e. where `__thread` is emulated with
72 : : // lower level API) (Mac, Windows), we use the platform runtime API to create
73 : : // TLS variable directly.
74 : : // This is functionally equivalent to using `__thread` but can be
75 : : // more efficient since we can have better control over the creation and
76 : : // initialization of the TLS buffer.
77 : : //
78 : : // On platforms that use ELF (Linux, FreeBSD), we use a `__thread` variable
79 : : // as the fallback in the shared object. For better efficiency, we also
80 : : // create a `__thread` variable in the main executable using a static TLS
81 : : // model.
82 : : #if defined(_OS_DARWIN_)
83 : : // Mac doesn't seem to have static TLS model so the runtime TLS getter
84 : : // registration will only add overhead to TLS access. The `__thread` variables
85 : : // are emulated with `pthread_key_t` so it is actually faster to use it directly.
86 : : static pthread_key_t jl_pgcstack_key;
87 : :
88 : : __attribute__((constructor)) void jl_init_tls(void)
89 : : {
90 : : pthread_key_create(&jl_pgcstack_key, NULL);
91 : : }
92 : :
93 : : JL_CONST_FUNC jl_gcframe_t **jl_get_pgcstack(void) JL_NOTSAFEPOINT
94 : : {
95 : : return (jl_gcframe_t**)pthread_getspecific(jl_pgcstack_key);
96 : : }
97 : :
98 : : void jl_set_pgcstack(jl_gcframe_t **pgcstack) JL_NOTSAFEPOINT
99 : : {
100 : : pthread_setspecific(jl_pgcstack_key, (void*)pgcstack);
101 : : }
102 : :
103 : : void jl_pgcstack_getkey(jl_get_pgcstack_func **f, pthread_key_t *k)
104 : : {
105 : : // for codegen
106 : : *f = pthread_getspecific;
107 : : *k = jl_pgcstack_key;
108 : : }
109 : :
110 : :
111 : : JL_DLLEXPORT void jl_pgcstack_setkey(jl_get_pgcstack_func *f, pthread_key_t k)
112 : : {
113 : : jl_safe_printf("ERROR: Attempt to change TLS address.\n");
114 : : }
115 : :
116 : : #elif defined(_OS_WINDOWS_)
117 : : // Apparently windows doesn't have a static TLS model (or one that can be
118 : : // reliably used from a shared library) either..... Use `TLSAlloc` instead.
119 : :
120 : : static DWORD jl_pgcstack_key;
121 : : static DWORD jl_safe_restore_key;
122 : :
123 : : // Put this here for now. We can move this out later if we find more use for it.
124 : : BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason,
125 : : IN LPVOID Reserved)
126 : : {
127 : : switch (nReason) {
128 : : case DLL_PROCESS_ATTACH:
129 : : jl_pgcstack_key = TlsAlloc();
130 : : assert(jl_pgcstack_key != TLS_OUT_OF_INDEXES);
131 : : jl_safe_restore_key = TlsAlloc();
132 : : assert(jl_safe_restore_key != TLS_OUT_OF_INDEXES);
133 : : // Fall through
134 : : case DLL_THREAD_ATTACH:
135 : : break;
136 : : case DLL_THREAD_DETACH:
137 : : break;
138 : : case DLL_PROCESS_DETACH:
139 : : TlsFree(jl_pgcstack_key);
140 : : TlsFree(jl_safe_restore_key);
141 : : break;
142 : : }
143 : : return 1; // success
144 : : }
145 : :
146 : : #if defined(_CPU_X86_64_)
147 : : #define SAVE_ERRNO \
148 : : DWORD *plast_error = (DWORD*)(__readgsqword(0x30) + 0x68); \
149 : : DWORD last_error = *plast_error
150 : : #define LOAD_ERRNO \
151 : : *plast_error = last_error
152 : : #elif defined(_CPU_X86_)
153 : : #define SAVE_ERRNO \
154 : : DWORD *plast_error = (DWORD*)(__readfsdword(0x18) + 0x34); \
155 : : DWORD last_error = *plast_error
156 : : #define LOAD_ERRNO \
157 : : *plast_error = last_error
158 : : #else
159 : : #define SAVE_ERRNO \
160 : : DWORD last_error = GetLastError()
161 : : #define LOAD_ERRNO \
162 : : SetLastError(last_error)
163 : : #endif
164 : :
165 : : JL_DLLEXPORT jl_jmp_buf *jl_get_safe_restore(void)
166 : : {
167 : : SAVE_ERRNO;
168 : : jl_jmp_buf *sr = (jl_jmp_buf*)TlsGetValue(jl_safe_restore_key);
169 : : LOAD_ERRNO;
170 : : return sr;
171 : : }
172 : :
173 : : JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *sr)
174 : : {
175 : : SAVE_ERRNO;
176 : : TlsSetValue(jl_safe_restore_key, (void*)sr);
177 : : LOAD_ERRNO;
178 : : }
179 : :
180 : : JL_CONST_FUNC jl_gcframe_t **jl_get_pgcstack(void) JL_NOTSAFEPOINT
181 : : {
182 : : SAVE_ERRNO;
183 : : jl_gcframe_t **pgcstack = (jl_gcframe_t**)TlsGetValue(jl_pgcstack_key);
184 : : LOAD_ERRNO;
185 : : return pgcstack;
186 : : }
187 : :
188 : : void jl_set_pgcstack(jl_gcframe_t **pgcstack) JL_NOTSAFEPOINT
189 : : {
190 : : // n.b.: this smashes GetLastError
191 : : TlsSetValue(jl_pgcstack_key, (void*)pgcstack);
192 : : }
193 : :
194 : : void jl_pgcstack_getkey(jl_get_pgcstack_func **f, DWORD *k)
195 : : {
196 : : // for codegen
197 : : *f = jl_get_pgcstack;
198 : : *k = jl_pgcstack_key;
199 : : }
200 : :
201 : : JL_DLLEXPORT void jl_pgcstack_setkey(jl_get_pgcstack_func *f, DWORD k)
202 : : {
203 : : jl_safe_printf("ERROR: Attempt to change TLS address.\n");
204 : : }
205 : :
206 : :
207 : : #else
208 : : // We use the faster static version in the main executable to replace
209 : : // the slower version in the shared object. The code in different libraries
210 : : // or executables, however, have to agree on which version to use.
211 : : // The general solution is to add one more indirection in the C entry point.
212 : : //
213 : : // When `ifunc` is available, we can use it to trick the linker to use the
214 : : // real address (`jl_get_pgcstack_static`) directly as the symbol address.
215 : : //
216 : : // However, since the detection of the static version in `ifunc`
217 : : // is not guaranteed to be reliable, we still need to fallback to the wrapper
218 : : // version as the symbol address if we didn't find the static version in `ifunc`.
219 : :
220 : : // fallback provided for embedding
221 : : static jl_pgcstack_key_t jl_pgcstack_key;
222 : : static __thread jl_gcframe_t **pgcstack_;
223 : 0 : static jl_gcframe_t **jl_get_pgcstack_fallback(void) JL_NOTSAFEPOINT
224 : : {
225 : 0 : return pgcstack_;
226 : : }
227 : 0 : static jl_gcframe_t ***jl_pgcstack_addr_fallback(void) JL_NOTSAFEPOINT
228 : : {
229 : 0 : return &pgcstack_;
230 : : }
231 : 3127 : void jl_set_pgcstack(jl_gcframe_t **pgcstack) JL_NOTSAFEPOINT
232 : : {
233 : 3127 : *jl_pgcstack_key() = pgcstack;
234 : 3127 : }
235 : : # if JL_USE_IFUNC
236 : : JL_DLLEXPORT __attribute__((weak))
237 : : void jl_register_pgcstack_getter(void);
238 : : # endif
239 : : static jl_gcframe_t **jl_get_pgcstack_init(void);
240 : : static jl_get_pgcstack_func *jl_get_pgcstack_cb = jl_get_pgcstack_init;
241 : 0 : static jl_gcframe_t **jl_get_pgcstack_init(void)
242 : : {
243 : : // This 2-step initialization is used to detect calling
244 : : // `jl_pgcstack_getkey` after the address of the TLS variables
245 : : // are used. Since the address of TLS variables should be constant,
246 : : // changing the getter address can result in weird crashes.
247 : :
248 : : // This is clearly not thread safe but should be fine since we
249 : : // make sure the tls states callback is finalized before adding
250 : : // multiple threads
251 : : # if JL_USE_IFUNC
252 [ # # ]: 0 : if (jl_register_pgcstack_getter)
253 : 0 : jl_register_pgcstack_getter();
254 : : else
255 : : # endif
256 : : {
257 : 0 : jl_get_pgcstack_cb = jl_get_pgcstack_fallback;
258 : 0 : jl_pgcstack_key = &jl_pgcstack_addr_fallback;
259 : : }
260 : 0 : return jl_get_pgcstack_cb();
261 : : }
262 : :
263 : 15 : JL_DLLEXPORT void jl_pgcstack_setkey(jl_get_pgcstack_func *f, jl_pgcstack_key_t k)
264 : : {
265 [ + - - + ]: 15 : if (f == jl_get_pgcstack_cb || !f)
266 : 0 : return;
267 : : // only allow setting this once
268 [ - + ]: 15 : if (jl_get_pgcstack_cb != jl_get_pgcstack_init) {
269 : 0 : jl_safe_printf("ERROR: Attempt to change TLS address.\n");
270 : 0 : exit(1);
271 : : }
272 : 15 : jl_get_pgcstack_cb = f;
273 : 15 : jl_pgcstack_key = k;
274 : : }
275 : :
276 : 6059010000 : JL_DLLEXPORT jl_gcframe_t **jl_get_pgcstack(void) JL_GLOBALLY_ROOTED
277 : : {
278 : : #ifndef __clang_gcanalyzer__
279 : 6059010000 : return jl_get_pgcstack_cb();
280 : : #endif
281 : : }
282 : :
283 : 17 : void jl_pgcstack_getkey(jl_get_pgcstack_func **f, jl_pgcstack_key_t *k)
284 : : {
285 [ - + ]: 17 : if (jl_get_pgcstack_cb == jl_get_pgcstack_init)
286 : 0 : jl_get_pgcstack_init();
287 : : // for codegen
288 : 17 : *f = jl_get_pgcstack_cb;
289 : 17 : *k = jl_pgcstack_key;
290 : 17 : }
291 : : #endif
292 : :
293 : : static uv_mutex_t tls_lock; // controls write-access to these variables:
294 : : jl_ptls_t *jl_all_tls_states JL_GLOBALLY_ROOTED;
295 : : static uv_cond_t cond;
296 : :
297 : : // return calling thread's ID
298 : 0 : JL_DLLEXPORT int16_t jl_threadid(void)
299 : : {
300 : 0 : return jl_atomic_load_relaxed(&jl_current_task->tid);
301 : : }
302 : :
303 : 29766 : JL_DLLEXPORT int8_t jl_threadpoolid(int16_t tid) JL_NOTSAFEPOINT
304 : : {
305 [ + - - + ]: 29766 : if (tid < 0 || tid >= jl_n_threads)
306 : 0 : jl_error("invalid tid");
307 : 29766 : int n = 0;
308 [ + - ]: 29766 : for (int i = 0; i < jl_n_threadpools; i++) {
309 : 29766 : n += jl_n_threads_per_pool[i];
310 [ + - ]: 29766 : if (tid < n)
311 : 29766 : return (int8_t)i;
312 : : }
313 : 0 : jl_error("internal error: couldn't determine threadpool id");
314 : : }
315 : :
316 : 15 : jl_ptls_t jl_init_threadtls(int16_t tid)
317 : : {
318 : 15 : jl_ptls_t ptls = (jl_ptls_t)calloc(1, sizeof(jl_tls_states_t));
319 : 15 : ptls->system_id = (jl_thread_t)(uintptr_t)uv_thread_self();
320 : 15 : ptls->rngseed = jl_rand();
321 : : #ifdef _OS_WINDOWS_
322 : : if (tid == 0) {
323 : : if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
324 : : GetCurrentProcess(), &hMainThread, 0,
325 : : FALSE, DUPLICATE_SAME_ACCESS)) {
326 : : jl_printf(JL_STDERR, "WARNING: failed to access handle to main thread\n");
327 : : hMainThread = INVALID_HANDLE_VALUE;
328 : : }
329 : : }
330 : : #endif
331 : 15 : ptls->tid = tid;
332 : 15 : jl_atomic_store_relaxed(&ptls->gc_state, 0); // GC unsafe
333 : : // Conditionally initialize the safepoint address. See comment in
334 : : // `safepoint.c`
335 [ + - ]: 15 : if (tid == 0) {
336 : 15 : ptls->safepoint = (size_t*)(jl_safepoint_pages + jl_page_size);
337 : : }
338 : : else {
339 : 0 : ptls->safepoint = (size_t*)(jl_safepoint_pages + jl_page_size * 2 +
340 : : sizeof(size_t));
341 : : }
342 : : jl_bt_element_t *bt_data = (jl_bt_element_t*)
343 : 15 : malloc_s(sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1));
344 : 15 : memset(bt_data, 0, sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1));
345 : 15 : ptls->bt_data = bt_data;
346 : 15 : small_arraylist_new(&ptls->locks, 0);
347 : 15 : jl_init_thread_heap(ptls);
348 : :
349 : 15 : uv_mutex_init(&ptls->sleep_lock);
350 : 15 : uv_cond_init(&ptls->wake_signal);
351 : :
352 : 15 : jl_all_tls_states[tid] = ptls;
353 : :
354 : 15 : return ptls;
355 : : }
356 : :
357 : : JL_DLLEXPORT jl_mutex_t jl_codegen_lock;
358 : : jl_mutex_t typecache_lock;
359 : :
360 : : JL_DLLEXPORT ssize_t jl_tls_offset = -1;
361 : :
362 : : #ifdef JL_ELF_TLS_VARIANT
363 : : JL_DLLEXPORT const int jl_tls_elf_support = 1;
364 : : // Optimize TLS access in codegen if the TLS buffer is using a IE or LE model.
365 : : // To detect such case, we find the size of the TLS segment in the main
366 : : // executable and the thread pointer (TP) and then see if the TLS pointer on the
367 : : // current thread is in the right range.
368 : : // This can in principle be extended to the case where the TLS buffer is
369 : : // in the shared library but is part of the static buffer but that seems harder
370 : : // to detect.
371 : : # if JL_ELF_TLS_VARIANT == 1
372 : : // In Variant 1, the static TLS buffer comes after a fixed size TCB.
373 : : // The alignment needs to be applied to the original size.
374 : : static inline size_t jl_add_tls_size(size_t orig_size, size_t size, size_t align)
375 : : {
376 : : return LLT_ALIGN(orig_size, align) + size;
377 : : }
378 : : static inline ssize_t jl_check_tls_bound(void *tp, jl_gcframe_t ***k0, size_t tls_size)
379 : : {
380 : : ssize_t offset = (char*)k0 - (char*)tp;
381 : : if (offset < JL_ELF_TLS_INIT_SIZE ||
382 : : (size_t)offset + sizeof(*k0) > tls_size)
383 : : return -1;
384 : : return offset;
385 : : }
386 : : # elif JL_ELF_TLS_VARIANT == 2
387 : : // In Variant 2, the static TLS buffer comes before a unknown size TCB.
388 : : // The alignment needs to be applied to the new size.
389 : 15 : static inline size_t jl_add_tls_size(size_t orig_size, size_t size, size_t align)
390 : : {
391 : 15 : return LLT_ALIGN(orig_size + size, align);
392 : : }
393 : 15 : static inline ssize_t jl_check_tls_bound(void *tp, jl_gcframe_t ***k0, size_t tls_size)
394 : : {
395 : 15 : ssize_t offset = (char*)tp - (char*)k0;
396 [ + - - + ]: 15 : if (offset < sizeof(*k0) || offset > tls_size)
397 : 0 : return -1;
398 : 15 : return -offset;
399 : : }
400 : : # else
401 : : # error "Unknown static TLS variant"
402 : : # endif
403 : :
404 : : // Find the size of the TLS segment in the main executable
405 : : typedef struct {
406 : : size_t total_size;
407 : : } check_tls_cb_t;
408 : :
409 : 15 : static int check_tls_cb(struct dl_phdr_info *info, size_t size, void *_data)
410 : : {
411 : 15 : check_tls_cb_t *data = (check_tls_cb_t*)_data;
412 : 15 : const ElfW(Phdr) *phdr = info->dlpi_phdr;
413 : 15 : unsigned phnum = info->dlpi_phnum;
414 : 15 : size_t total_size = JL_ELF_TLS_INIT_SIZE;
415 : :
416 [ + + ]: 225 : for (unsigned i = 0; i < phnum; i++) {
417 : 210 : const ElfW(Phdr) *seg = &phdr[i];
418 [ + + ]: 210 : if (seg->p_type != PT_TLS)
419 : 195 : continue;
420 : : // There should be only one TLS segment
421 : : // Variant II
422 : 15 : total_size = jl_add_tls_size(total_size, seg->p_memsz, seg->p_align);
423 : : }
424 : 15 : data->total_size = total_size;
425 : : // only run once (on the main executable)
426 : 15 : return 1;
427 : : }
428 : :
429 : 15 : static void jl_check_tls(void)
430 : : {
431 : : jl_get_pgcstack_func *f;
432 : : jl_gcframe_t ***(*k)(void);
433 : 15 : jl_pgcstack_getkey(&f, &k);
434 : 15 : jl_gcframe_t ***k0 = k();
435 [ - + ]: 15 : if (k0 == NULL)
436 : 0 : return;
437 : 15 : check_tls_cb_t data = {0};
438 : 15 : dl_iterate_phdr(check_tls_cb, &data);
439 [ - + ]: 15 : if (data.total_size == 0)
440 : 0 : return;
441 : : void *tp; // Thread pointer
442 : : #if defined(_CPU_X86_64_)
443 : 15 : asm("movq %%fs:0, %0" : "=r"(tp));
444 : : #elif defined(_CPU_X86_)
445 : : asm("movl %%gs:0, %0" : "=r"(tp));
446 : : #elif defined(_CPU_AARCH64_)
447 : : asm("mrs %0, tpidr_el0" : "=r"(tp));
448 : : #elif defined(__ARM_ARCH) && __ARM_ARCH >= 7
449 : : asm("mrc p15, 0, %0, c13, c0, 3" : "=r"(tp));
450 : : #else
451 : : # error "Cannot emit thread pointer for this architecture."
452 : : #endif
453 : 15 : ssize_t offset = jl_check_tls_bound(tp, k0, data.total_size);
454 [ - + ]: 15 : if (offset == -1)
455 : 0 : return;
456 : 15 : jl_tls_offset = offset;
457 : : }
458 : : #else
459 : : // !JL_ELF_TLS_VARIANT
460 : : JL_DLLEXPORT const int jl_tls_elf_support = 0;
461 : : #endif
462 : :
463 : : // interface to Julia; sets up to make the runtime thread-safe
464 : 15 : void jl_init_threading(void)
465 : : {
466 : : char *cp;
467 : :
468 : 15 : uv_mutex_init(&tls_lock);
469 : 15 : uv_cond_init(&cond);
470 : :
471 : : #ifdef JL_ELF_TLS_VARIANT
472 : 15 : jl_check_tls();
473 : : #endif
474 : :
475 : : // Determine how many threads and pools are requested. This may have been
476 : : // specified on the command line (and so are in `jl_options`) or by the
477 : : // environment variable. Set the globals `jl_n_threadpools`, `jl_n_threads`
478 : : // and `jl_n_threads_per_pool`.
479 : 15 : jl_n_threadpools = 1;
480 : 15 : jl_n_threads = JULIA_NUM_THREADS;
481 : 15 : int16_t nthreads = jl_n_threads, nthreadsi = 0;
482 : : char *endptr, *endptri;
483 : :
484 [ - + ]: 15 : if (jl_options.nthreads != 0) { // --threads specified
485 : 0 : jl_n_threadpools = jl_options.nthreadpools;
486 : 0 : nthreads = jl_options.nthreads_per_pool[0];
487 [ # # ]: 0 : if (nthreads < 0)
488 : 0 : nthreads = jl_effective_threads();
489 [ # # ]: 0 : if (jl_n_threadpools == 2)
490 : 0 : nthreadsi = jl_options.nthreads_per_pool[1];
491 : : }
492 [ + + ]: 15 : else if ((cp = getenv(NUM_THREADS_NAME))) { // ENV[NUM_THREADS_NAME] specified
493 [ - + ]: 10 : if (!strncmp(cp, "auto", 4)) {
494 : 0 : nthreads = jl_effective_threads();
495 : 0 : cp += 4;
496 : : }
497 : : else {
498 : 10 : errno = 0;
499 : 10 : nthreads = strtol(cp, &endptr, 10);
500 [ + - + - : 10 : if (errno != 0 || endptr == cp || nthreads <= 0)
- + ]
501 : 0 : nthreads = 1;
502 : 10 : cp = endptr;
503 : : }
504 [ - + ]: 10 : if (*cp == ',') {
505 : 0 : cp++;
506 [ # # ]: 0 : if (!strncmp(cp, "auto", 4))
507 : 0 : nthreadsi = 1;
508 : : else {
509 : 0 : errno = 0;
510 : 0 : nthreadsi = strtol(cp, &endptri, 10);
511 [ # # # # : 0 : if (errno != 0 || endptri == cp || nthreadsi < 0)
# # ]
512 : 0 : nthreadsi = 0;
513 : : }
514 [ # # ]: 0 : if (nthreadsi > 0)
515 : 0 : jl_n_threadpools++;
516 : : }
517 : : }
518 : :
519 : 15 : jl_n_threads = nthreads + nthreadsi;
520 : 15 : jl_n_threads_per_pool = (int *)malloc(2 * sizeof(int));
521 : 15 : jl_n_threads_per_pool[0] = nthreads;
522 : 15 : jl_n_threads_per_pool[1] = nthreadsi;
523 : :
524 : : #ifndef __clang_gcanalyzer__
525 : 15 : jl_all_tls_states = (jl_ptls_t*)calloc(jl_n_threads, sizeof(void*));
526 : : #endif
527 : 15 : }
528 : :
529 : : static uv_barrier_t thread_init_done;
530 : :
531 : 15 : void jl_start_threads(void)
532 : : {
533 : 15 : int cpumasksize = uv_cpumask_size();
534 : : char *cp;
535 : : int i, exclusive;
536 : : uv_thread_t uvtid;
537 [ - + ]: 15 : if (cpumasksize < jl_n_threads) // also handles error case
538 : 0 : cpumasksize = jl_n_threads;
539 : 15 : char *mask = (char*)alloca(cpumasksize);
540 : :
541 : : // do we have exclusive use of the machine? default is no
542 : 15 : exclusive = DEFAULT_MACHINE_EXCLUSIVE;
543 : 15 : cp = getenv(MACHINE_EXCLUSIVE_NAME);
544 [ - + - - ]: 15 : if (cp && strcmp(cp, "0") != 0)
545 : 0 : exclusive = 1;
546 : :
547 : : // exclusive use: affinitize threads, master thread on proc 0, rest
548 : : // according to a 'compact' policy
549 : : // non-exclusive: no affinity settings; let the kernel move threads about
550 [ - + ]: 15 : if (exclusive) {
551 [ # # ]: 0 : if (jl_n_threads > jl_cpu_threads()) {
552 : 0 : jl_printf(JL_STDERR, "ERROR: Too many threads requested for %s option.\n", MACHINE_EXCLUSIVE_NAME);
553 : 0 : exit(1);
554 : : }
555 : 0 : memset(mask, 0, cpumasksize);
556 : 0 : mask[0] = 1;
557 : 0 : uvtid = uv_thread_self();
558 : 0 : uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize);
559 : 0 : mask[0] = 0;
560 : : }
561 : :
562 : : // The analyzer doesn't know jl_n_threads doesn't change, help it
563 : 15 : size_t nthreads = jl_n_threads;
564 : :
565 : : // create threads
566 : 15 : uv_barrier_init(&thread_init_done, nthreads);
567 : :
568 [ - + ]: 15 : for (i = 1; i < nthreads; ++i) {
569 : 0 : jl_threadarg_t *t = (jl_threadarg_t *)malloc_s(sizeof(jl_threadarg_t)); // ownership will be passed to the thread
570 : 0 : t->tid = i;
571 : 0 : t->barrier = &thread_init_done;
572 : 0 : uv_thread_create(&uvtid, jl_threadfun, t);
573 [ # # ]: 0 : if (exclusive) {
574 : 0 : mask[i] = 1;
575 : 0 : uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize);
576 : 0 : mask[i] = 0;
577 : : }
578 : 0 : uv_thread_detach(&uvtid);
579 : : }
580 : :
581 : 15 : uv_barrier_wait(&thread_init_done);
582 : 15 : }
583 : :
584 : : _Atomic(unsigned) _threadedregion; // HACK: keep track of whether to prioritize IO or threading
585 : :
586 : 0 : JL_DLLEXPORT int jl_in_threaded_region(void)
587 : : {
588 : 0 : return jl_atomic_load_relaxed(&_threadedregion) != 0;
589 : : }
590 : :
591 : 0 : JL_DLLEXPORT void jl_enter_threaded_region(void)
592 : : {
593 : 0 : jl_atomic_fetch_add(&_threadedregion, 1);
594 : 0 : }
595 : :
596 : 0 : JL_DLLEXPORT void jl_exit_threaded_region(void)
597 : : {
598 [ # # ]: 0 : if (jl_atomic_fetch_add(&_threadedregion, -1) == 1) {
599 : : // make sure no more callbacks will run while user code continues
600 : : // outside thread region and might touch an I/O object.
601 : 0 : JL_UV_LOCK();
602 : 0 : JL_UV_UNLOCK();
603 : : // make sure thread 0 is not using the sleep_lock
604 : : // so that it may enter the libuv event loop instead
605 : 0 : jl_wakeup_thread(0);
606 : : }
607 : 0 : }
608 : :
609 : 318368000 : void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint)
610 : : {
611 : 318368000 : jl_task_t *owner = jl_atomic_load_relaxed(&lock->owner);
612 [ + + ]: 318368000 : if (owner == self) {
613 : 77815500 : lock->count++;
614 : 77815500 : return;
615 : : }
616 : : while (1) {
617 [ + - + - ]: 240553000 : if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) {
618 : 240553000 : lock->count = 1;
619 : 240553000 : return;
620 : : }
621 [ # # ]: 0 : if (safepoint) {
622 : 0 : jl_gc_safepoint_(self->ptls);
623 : : }
624 [ # # ]: 0 : if (jl_running_under_rr(0)) {
625 : : // when running under `rr`, use system mutexes rather than spin locking
626 : 0 : uv_mutex_lock(&tls_lock);
627 [ # # ]: 0 : if (jl_atomic_load_relaxed(&lock->owner))
628 : 0 : uv_cond_wait(&cond, &tls_lock);
629 : 0 : uv_mutex_unlock(&tls_lock);
630 : : }
631 : : jl_cpu_pause();
632 : 0 : owner = jl_atomic_load_relaxed(&lock->owner);
633 : : }
634 : : }
635 : :
636 : 318430000 : static void jl_lock_frame_push(jl_task_t *self, jl_mutex_t *lock)
637 : : {
638 : 318430000 : jl_ptls_t ptls = self->ptls;
639 : 318430000 : small_arraylist_t *locks = &ptls->locks;
640 : 318430000 : uint32_t len = locks->len;
641 [ + + ]: 318430000 : if (__unlikely(len >= locks->max)) {
642 : 19 : small_arraylist_grow(locks, 1);
643 : : }
644 : : else {
645 : 318429000 : locks->len = len + 1;
646 : : }
647 : 318430000 : locks->items[len] = (void*)lock;
648 : 318430000 : }
649 : :
650 : 318425000 : static void jl_lock_frame_pop(jl_task_t *self)
651 : : {
652 : 318425000 : jl_ptls_t ptls = self->ptls;
653 [ - + ]: 318425000 : assert(ptls->locks.len > 0);
654 : 318425000 : ptls->locks.len--;
655 : 318425000 : }
656 : :
657 : 318367000 : void _jl_mutex_lock(jl_task_t *self, jl_mutex_t *lock)
658 : : {
659 : 318367000 : JL_SIGATOMIC_BEGIN_self();
660 : 318367000 : _jl_mutex_wait(self, lock, 1);
661 : 318367000 : jl_lock_frame_push(self, lock);
662 : 318367000 : }
663 : :
664 : 62442 : int _jl_mutex_trylock_nogc(jl_task_t *self, jl_mutex_t *lock)
665 : : {
666 : 62442 : jl_task_t *owner = jl_atomic_load_acquire(&lock->owner);
667 [ + + ]: 62442 : if (owner == self) {
668 : 12115 : lock->count++;
669 : 12115 : return 1;
670 : : }
671 [ + - + - ]: 50327 : if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) {
672 : 50327 : lock->count = 1;
673 : 50327 : return 1;
674 : : }
675 : 0 : return 0;
676 : : }
677 : :
678 : 62442 : int _jl_mutex_trylock(jl_task_t *self, jl_mutex_t *lock)
679 : : {
680 : 62442 : int got = _jl_mutex_trylock_nogc(self, lock);
681 [ + - ]: 62442 : if (got) {
682 : 62442 : JL_SIGATOMIC_BEGIN_self();
683 : 62442 : jl_lock_frame_push(self, lock);
684 : : }
685 : 62442 : return got;
686 : : }
687 : :
688 : 318431000 : void _jl_mutex_unlock_nogc(jl_mutex_t *lock)
689 : : {
690 : : #ifndef __clang_gcanalyzer__
691 [ + - ]: 318431000 : assert(jl_atomic_load_relaxed(&lock->owner) == jl_current_task &&
692 : : "Unlocking a lock in a different thread.");
693 [ + + ]: 318431000 : if (--lock->count == 0) {
694 : 240603000 : jl_atomic_store_release(&lock->owner, (jl_task_t*)NULL);
695 : : jl_cpu_wake();
696 [ - + ]: 240603000 : if (jl_running_under_rr(0)) {
697 : : // when running under `rr`, use system mutexes rather than spin locking
698 : 0 : uv_mutex_lock(&tls_lock);
699 : 0 : uv_cond_broadcast(&cond);
700 : 0 : uv_mutex_unlock(&tls_lock);
701 : : }
702 : : }
703 : : #endif
704 : 318431000 : }
705 : :
706 : 318425000 : void _jl_mutex_unlock(jl_task_t *self, jl_mutex_t *lock)
707 : : {
708 : 318425000 : _jl_mutex_unlock_nogc(lock);
709 : 318425000 : jl_lock_frame_pop(self);
710 [ + + ]: 318425000 : JL_SIGATOMIC_END_self();
711 [ + + ]: 318425000 : if (jl_atomic_load_relaxed(&jl_gc_have_pending_finalizers)) {
712 : 54805600 : jl_gc_run_pending_finalizers(self); // may GC
713 : : }
714 : 318425000 : }
715 : :
716 : :
717 : : // Make gc alignment available for threading
718 : : // see threads.jl alignment
719 : 56 : JL_DLLEXPORT int jl_alignment(size_t sz)
720 : : {
721 : 56 : return jl_gc_alignment(sz);
722 : : }
723 : :
724 : : #ifdef __cplusplus
725 : : }
726 : : #endif
|