LCOV - code coverage report
Current view: top level - src - threading.c (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 168 251 66.9 %
Date: 2022-07-17 01:01:28 Functions: 25 32 78.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 50 112 44.6 %

           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

Generated by: LCOV version 1.14