LCOV - code coverage report
Current view: top level - src - task.c (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 278 479 58.0 %
Date: 2022-07-17 01:01:28 Functions: 22 36 61.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 86 234 36.8 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : /*
       4                 :            :   task.c
       5                 :            :   lightweight processes (symmetric coroutines)
       6                 :            : */
       7                 :            : 
       8                 :            : // need this to get the real definition of ucontext_t,
       9                 :            : // if we're going to use the ucontext_t implementation there
      10                 :            : //#if defined(__APPLE__) && defined(JL_HAVE_UCONTEXT)
      11                 :            : //#pragma push_macro("_XOPEN_SOURCE")
      12                 :            : //#define _XOPEN_SOURCE
      13                 :            : //#include <ucontext.h>
      14                 :            : //#pragma pop_macro("_XOPEN_SOURCE")
      15                 :            : //#endif
      16                 :            : 
      17                 :            : // this is needed for !COPY_STACKS to work on linux
      18                 :            : #ifdef _FORTIFY_SOURCE
      19                 :            : // disable __longjmp_chk validation so that we can jump between stacks
      20                 :            : // (which would normally be invalid to do with setjmp / longjmp)
      21                 :            : #pragma push_macro("_FORTIFY_SOURCE")
      22                 :            : #undef _FORTIFY_SOURCE
      23                 :            : #include <setjmp.h>
      24                 :            : #pragma pop_macro("_FORTIFY_SOURCE")
      25                 :            : #endif
      26                 :            : 
      27                 :            : #include "platform.h"
      28                 :            : 
      29                 :            : #include <stdlib.h>
      30                 :            : #include <string.h>
      31                 :            : #include <signal.h>
      32                 :            : #include <unistd.h>
      33                 :            : #include <errno.h>
      34                 :            : #include <inttypes.h>
      35                 :            : #include "julia.h"
      36                 :            : #include "julia_internal.h"
      37                 :            : #include "threading.h"
      38                 :            : #include "julia_assert.h"
      39                 :            : 
      40                 :            : #ifdef __cplusplus
      41                 :            : extern "C" {
      42                 :            : #endif
      43                 :            : 
      44                 :            : #if defined(_COMPILER_ASAN_ENABLED_)
      45                 :            : static inline void sanitizer_start_switch_fiber(const void* bottom, size_t size) {
      46                 :            :     __sanitizer_start_switch_fiber(NULL, bottom, size);
      47                 :            : }
      48                 :            : static inline void sanitizer_finish_switch_fiber(void) {
      49                 :            :     __sanitizer_finish_switch_fiber(NULL, NULL, NULL);
      50                 :            : }
      51                 :            : #else
      52                 :       3097 : static inline void sanitizer_start_switch_fiber(const void* bottom, size_t size) {}
      53                 :       3097 : static inline void sanitizer_finish_switch_fiber(void) {}
      54                 :            : #endif
      55                 :            : 
      56                 :            : #if defined(_COMPILER_TSAN_ENABLED_)
      57                 :            : // must defined as macros, since the function containing them must not return before the longjmp
      58                 :            : #define tsan_destroy_ctx(_ptls, _ctx) do { \
      59                 :            :         jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
      60                 :            :         if (_tsan_macro_ctx != &(_ptls)->root_task->ctx) { \
      61                 :            :             __tsan_destroy_fiber(_tsan_macro_ctx->tsan_state); \
      62                 :            :         } \
      63                 :            :         _tsan_macro_ctx->tsan_state = NULL; \
      64                 :            :     } while (0)
      65                 :            : #define tsan_switch_to_ctx(_ctx) do { \
      66                 :            :         jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
      67                 :            :         __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \
      68                 :            :     } while (0)
      69                 :            : #ifdef COPY_STACKS
      70                 :            : #define tsan_destroy_copyctx(_ptls, _ctx) do { \
      71                 :            :         jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
      72                 :            :         if (_tsan_macro_ctx != &(_ptls)->root_task->ctx) { \
      73                 :            :             __tsan_destroy_fiber(_tsan_macro_ctx->tsan_state); \
      74                 :            :         } \
      75                 :            :         _tsan_macro_ctx->tsan_state = NULL; \
      76                 :            :     } while (0)
      77                 :            : #define tsan_switch_to_copyctx(_ctx) do { \
      78                 :            :         struct jl_stack_context_t *_tsan_macro_ctx = (_ctx); \
      79                 :            :         __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \
      80                 :            :     } while (0)
      81                 :            : #endif
      82                 :            : #else
      83                 :            : // just do minimal type-checking on the arguments
      84                 :            : #define tsan_destroy_ctx(_ptls, _ctx) do { \
      85                 :            :         jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
      86                 :            :         (void)_tsan_macro_ctx; \
      87                 :            :     } while (0)
      88                 :            : #define tsan_switch_to_ctx(_ctx) do { \
      89                 :            :         jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
      90                 :            :         (void)_tsan_macro_ctx; \
      91                 :            :     } while (0)
      92                 :            : #ifdef COPY_STACKS
      93                 :            : #define tsan_destroy_copyctx(_ptls, _ctx) do { \
      94                 :            :         jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
      95                 :            :         (void)_tsan_macro_ctx; \
      96                 :            :     } while (0)
      97                 :            : #define tsan_switch_to_copyctx(_ctx) do { \
      98                 :            :         jl_ucontext_t *_tsan_macro_ctx = (_ctx); \
      99                 :            :         (void)_tsan_macro_ctx; \
     100                 :            :     } while (0)
     101                 :            : #endif
     102                 :            : #endif
     103                 :            : 
     104                 :            : // empirically, jl_finish_task needs about 64k stack space to infer/run
     105                 :            : // and additionally, gc-stack reserves 64k for the guard pages
     106                 :            : #if defined(MINSIGSTKSZ)
     107                 :            : #define MINSTKSZ (MINSIGSTKSZ > 131072 ? MINSIGSTKSZ : 131072)
     108                 :            : #else
     109                 :            : #define MINSTKSZ 131072
     110                 :            : #endif
     111                 :            : 
     112                 :            : #define ROOT_TASK_STACK_ADJUSTMENT 3000000
     113                 :            : 
     114                 :            : #ifdef JL_HAVE_ASYNCIFY
     115                 :            : // Switching logic is implemented in JavaScript
     116                 :            : #define STATIC_OR_JS JL_DLLEXPORT
     117                 :            : #else
     118                 :            : #define STATIC_OR_JS static
     119                 :            : #endif
     120                 :            : 
     121                 :            : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT;
     122                 :            : STATIC_OR_JS void jl_set_fiber(jl_ucontext_t *t);
     123                 :            : STATIC_OR_JS void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t);
     124                 :            : STATIC_OR_JS void jl_start_fiber_swap(jl_ucontext_t *savet, jl_ucontext_t *t);
     125                 :            : STATIC_OR_JS void jl_start_fiber_set(jl_ucontext_t *t);
     126                 :            : 
     127                 :            : #ifdef ALWAYS_COPY_STACKS
     128                 :            : # ifndef COPY_STACKS
     129                 :            : # error "ALWAYS_COPY_STACKS requires COPY_STACKS"
     130                 :            : # endif
     131                 :            : static int always_copy_stacks = 1;
     132                 :            : #else
     133                 :            : static int always_copy_stacks = 0;
     134                 :            : #endif
     135                 :            : 
     136                 :            : #ifdef COPY_STACKS
     137                 :          0 : static void memcpy_a16(uint64_t *to, uint64_t *from, size_t nb)
     138                 :            : {
     139                 :          0 :     memcpy((char*)jl_assume_aligned(to, 16), (char*)jl_assume_aligned(from, 16), nb);
     140                 :            :     //uint64_t *end = (uint64_t*)((char*)from + nb);
     141                 :            :     //while (from < end)
     142                 :            :     //    *(to++) = *(from++);
     143                 :          0 : }
     144                 :            : 
     145                 :          0 : static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt)
     146                 :            : {
     147                 :          0 :     char *frame_addr = (char*)((uintptr_t)jl_get_frame_addr() & ~15);
     148                 :          0 :     char *stackbase = (char*)ptls->stackbase;
     149         [ #  # ]:          0 :     assert(stackbase > frame_addr);
     150                 :          0 :     size_t nb = stackbase - frame_addr;
     151                 :            :     void *buf;
     152         [ #  # ]:          0 :     if (lastt->bufsz < nb) {
     153                 :          0 :         buf = (void*)jl_gc_alloc_buf(ptls, nb);
     154                 :          0 :         lastt->stkbuf = buf;
     155                 :          0 :         lastt->bufsz = nb;
     156                 :            :     }
     157                 :            :     else {
     158                 :          0 :         buf = lastt->stkbuf;
     159                 :            :     }
     160                 :          0 :     *pt = NULL; // clear the gc-root for the target task before copying the stack for saving
     161                 :          0 :     lastt->copy_stack = nb;
     162                 :          0 :     lastt->sticky = 1;
     163                 :          0 :     memcpy_a16((uint64_t*)buf, (uint64_t*)frame_addr, nb);
     164                 :            :     // this task's stack could have been modified after
     165                 :            :     // it was marked by an incremental collection
     166                 :            :     // move the barrier back instead of walking it again here
     167                 :          0 :     jl_gc_wb_back(lastt);
     168                 :          0 : }
     169                 :            : 
     170                 :          0 : static void NOINLINE JL_NORETURN restore_stack(jl_task_t *t, jl_ptls_t ptls, char *p)
     171                 :            : {
     172                 :          0 :     size_t nb = t->copy_stack;
     173                 :          0 :     char *_x = (char*)ptls->stackbase - nb;
     174         [ #  # ]:          0 :     if (!p) {
     175                 :            :         // switch to a stackframe that's beyond the bounds of the last switch
     176                 :          0 :         p = _x;
     177         [ #  # ]:          0 :         if ((char*)&_x > _x) {
     178                 :          0 :             p = (char*)alloca((char*)&_x - _x);
     179                 :            :         }
     180                 :          0 :         restore_stack(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca
     181                 :            :     }
     182                 :          0 :     void *_y = t->stkbuf;
     183   [ #  #  #  # ]:          0 :     assert(_x != NULL && _y != NULL);
     184                 :          0 :     memcpy_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe
     185                 :            : 
     186                 :          0 :     sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
     187                 :            : #if defined(_OS_WINDOWS_)
     188                 :            :     jl_setcontext(&t->ctx.copy_ctx);
     189                 :            : #else
     190                 :          0 :     jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1);
     191                 :            : #endif
     192                 :            :     abort(); // unreachable
     193                 :            : }
     194                 :            : 
     195                 :          0 : static void restore_stack2(jl_task_t *t, jl_ptls_t ptls, jl_task_t *lastt)
     196                 :            : {
     197   [ #  #  #  # ]:          0 :     assert(t->copy_stack && !lastt->copy_stack);
     198                 :          0 :     size_t nb = t->copy_stack;
     199                 :          0 :     char *_x = (char*)ptls->stackbase - nb;
     200                 :          0 :     void *_y = t->stkbuf;
     201   [ #  #  #  # ]:          0 :     assert(_x != NULL && _y != NULL);
     202                 :          0 :     memcpy_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe
     203                 :            : #if defined(JL_HAVE_UNW_CONTEXT)
     204                 :            :     volatile int returns = 0;
     205                 :            :     int r = unw_getcontext(&lastt->ctx.ctx);
     206                 :            :     if (++returns == 2) // r is garbage after the first return
     207                 :            :         return;
     208                 :            :     if (r != 0 || returns != 1)
     209                 :            :         abort();
     210                 :            : #elif defined(JL_HAVE_ASM) || defined(JL_HAVE_SIGALTSTACK) || defined(_OS_WINDOWS_)
     211         [ #  # ]:          0 :     if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0))
     212                 :          0 :         return;
     213                 :            : #else
     214                 :            : #error COPY_STACKS is incompatible with this platform
     215                 :            : #endif
     216                 :          0 :     sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
     217                 :          0 :     tsan_switch_to_copyctx(&t->ctx);
     218                 :            : #if defined(_OS_WINDOWS_)
     219                 :            :     jl_setcontext(&t->ctx.copy_ctx);
     220                 :            : #else
     221                 :          0 :     jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1);
     222                 :            : #endif
     223                 :            : }
     224                 :            : #endif
     225                 :            : 
     226                 :            : /* Rooted by the base module */
     227                 :            : static _Atomic(jl_function_t*) task_done_hook_func JL_GLOBALLY_ROOTED = NULL;
     228                 :            : 
     229                 :        130 : void JL_NORETURN jl_finish_task(jl_task_t *t)
     230                 :            : {
     231                 :        130 :     jl_task_t *ct = jl_current_task;
     232                 :            :     JL_PROBE_RT_FINISH_TASK(ct);
     233                 :        130 :     JL_SIGATOMIC_BEGIN();
     234         [ -  + ]:        130 :     if (jl_atomic_load_relaxed(&t->_isexception))
     235                 :          0 :         jl_atomic_store_release(&t->_state, JL_TASK_STATE_FAILED);
     236                 :            :     else
     237                 :        130 :         jl_atomic_store_release(&t->_state, JL_TASK_STATE_DONE);
     238         [ -  + ]:        130 :     if (t->copy_stack) // early free of stkbuf
     239                 :          0 :         t->stkbuf = NULL;
     240                 :            :     // ensure that state is cleared
     241                 :        130 :     ct->ptls->in_finalizer = 0;
     242                 :        130 :     ct->ptls->in_pure_callback = 0;
     243                 :        130 :     ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
     244                 :            :     // let the runtime know this task is dead and find a new task to run
     245                 :        130 :     jl_function_t *done = jl_atomic_load_relaxed(&task_done_hook_func);
     246         [ +  + ]:        130 :     if (done == NULL) {
     247                 :          4 :         done = (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("task_done_hook"));
     248         [ +  - ]:          4 :         if (done != NULL)
     249                 :          4 :             jl_atomic_store_release(&task_done_hook_func, done);
     250                 :            :     }
     251         [ +  - ]:        130 :     if (done != NULL) {
     252                 :        130 :         jl_value_t *args[2] = {done, (jl_value_t*)t};
     253   [ +  -  +  - ]:        130 :         JL_TRY {
     254                 :        130 :             jl_apply(args, 2);
     255                 :            :         }
     256         [ #  # ]:          0 :         JL_CATCH {
     257                 :          0 :             jl_no_exc_handler(jl_current_exception());
     258                 :            :         }
     259                 :            :     }
     260                 :          0 :     jl_gc_debug_critical_error();
     261                 :          0 :     abort();
     262                 :            : }
     263                 :            : 
     264                 :          0 : JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *ptid)
     265                 :            : {
     266                 :          0 :     size_t off = 0;
     267                 :            : #ifndef _OS_WINDOWS_
     268         [ #  # ]:          0 :     if (jl_all_tls_states[0]->root_task == task) {
     269                 :            :         // See jl_init_root_task(). The root task of the main thread
     270                 :            :         // has its buffer enlarged by an artificial 3000000 bytes, but
     271                 :            :         // that means that the start of the buffer usually points to
     272                 :            :         // inaccessible memory. We need to correct for this.
     273                 :          0 :         off = ROOT_TASK_STACK_ADJUSTMENT;
     274                 :            :     }
     275                 :            : #endif
     276                 :          0 :     jl_ptls_t ptls2 = task->ptls;
     277                 :          0 :     *ptid = -1;
     278         [ #  # ]:          0 :     if (ptls2) {
     279                 :          0 :         *ptid = jl_atomic_load_relaxed(&task->tid);
     280                 :            : #ifdef COPY_STACKS
     281         [ #  # ]:          0 :         if (task->copy_stack) {
     282                 :          0 :             *size = ptls2->stacksize;
     283                 :          0 :             return (char *)ptls2->stackbase - *size;
     284                 :            :         }
     285                 :            : #endif
     286                 :            :     }
     287                 :          0 :     *size = task->bufsz - off;
     288                 :          0 :     return (void *)((char *)task->stkbuf + off);
     289                 :            : }
     290                 :            : 
     291                 :          0 : JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task,
     292                 :            :                                        char **active_start, char **active_end,
     293                 :            :                                        char **total_start, char **total_end)
     294                 :            : {
     295         [ #  # ]:          0 :     if (!task->started) {
     296                 :          0 :         *total_start = *active_start = 0;
     297                 :          0 :         *total_end = *active_end = 0;
     298                 :          0 :         return;
     299                 :            :     }
     300                 :            : 
     301                 :          0 :     jl_ptls_t ptls2 = task->ptls;
     302   [ #  #  #  # ]:          0 :     if (task->copy_stack && ptls2) {
     303                 :          0 :         *total_start = *active_start = (char*)ptls2->stackbase - ptls2->stacksize;
     304                 :          0 :         *total_end = *active_end = (char*)ptls2->stackbase;
     305                 :            :     }
     306         [ #  # ]:          0 :     else if (task->stkbuf) {
     307                 :          0 :         *total_start = *active_start = (char*)task->stkbuf;
     308                 :            : #ifndef _OS_WINDOWS_
     309         [ #  # ]:          0 :         if (jl_all_tls_states[0]->root_task == task) {
     310                 :            :             // See jl_init_root_task(). The root task of the main thread
     311                 :            :             // has its buffer enlarged by an artificial 3000000 bytes, but
     312                 :            :             // that means that the start of the buffer usually points to
     313                 :            :             // inaccessible memory. We need to correct for this.
     314                 :          0 :             *active_start += ROOT_TASK_STACK_ADJUSTMENT;
     315                 :          0 :             *total_start += ROOT_TASK_STACK_ADJUSTMENT;
     316                 :            :         }
     317                 :            : #endif
     318                 :            : 
     319                 :          0 :         *total_end = *active_end = (char*)task->stkbuf + task->bufsz;
     320                 :            : #ifdef COPY_STACKS
     321                 :            :         // save_stack stores the stack of an inactive task in stkbuf, and the
     322                 :            :         // actual number of used bytes in copy_stack.
     323         [ #  # ]:          0 :         if (task->copy_stack > 1)
     324                 :          0 :             *active_end = (char*)task->stkbuf + task->copy_stack;
     325                 :            : #endif
     326                 :            :     }
     327                 :            :     else {
     328                 :            :         // no stack allocated yet
     329                 :          0 :         *total_start = *active_start = 0;
     330                 :          0 :         *total_end = *active_end = 0;
     331                 :          0 :         return;
     332                 :            :     }
     333                 :            : 
     334         [ #  # ]:          0 :     if (task == jl_current_task) {
     335                 :            :         // scan up to current `sp` for current thread and task
     336                 :          0 :         *active_start = (char*)jl_get_frame_addr();
     337                 :            :     }
     338                 :            : }
     339                 :            : 
     340                 :            : // Marked noinline so we can consistently skip the associated frame.
     341                 :            : // `skip` is number of additional frames to skip.
     342                 :       9000 : NOINLINE static void record_backtrace(jl_ptls_t ptls, int skip) JL_NOTSAFEPOINT
     343                 :            : {
     344                 :            :     // storing bt_size in ptls ensures roots in bt_data will be found
     345                 :       9000 :     ptls->bt_size = rec_backtrace(ptls->bt_data, JL_MAX_BT_SIZE, skip + 1);
     346                 :       9000 : }
     347                 :            : 
     348                 :          0 : JL_DLLEXPORT void jl_set_next_task(jl_task_t *task) JL_NOTSAFEPOINT
     349                 :            : {
     350                 :          0 :     jl_current_task->ptls->next_task = task;
     351                 :          0 : }
     352                 :            : 
     353                 :          0 : JL_DLLEXPORT jl_task_t *jl_get_next_task(void) JL_NOTSAFEPOINT
     354                 :            : {
     355                 :          0 :     jl_task_t *ct = jl_current_task;
     356         [ #  # ]:          0 :     if (ct->ptls->next_task)
     357                 :          0 :         return ct->ptls->next_task;
     358                 :          0 :     return ct;
     359                 :            : }
     360                 :            : 
     361                 :            : #ifdef _COMPILER_TSAN_ENABLED_
     362                 :            : const char tsan_state_corruption[] = "TSAN state corrupted. Exiting HARD!\n";
     363                 :            : #endif
     364                 :            : 
     365                 :       3097 : static void ctx_switch(jl_task_t *lastt)
     366                 :            : {
     367                 :       3097 :     jl_ptls_t ptls = lastt->ptls;
     368                 :       3097 :     jl_task_t **pt = &ptls->next_task;
     369                 :       3097 :     jl_task_t *t = *pt;
     370         [ -  + ]:       3097 :     assert(t != lastt);
     371                 :            :     // none of these locks should be held across a task switch
     372         [ -  + ]:       3097 :     assert(ptls->locks.len == 0);
     373                 :            : 
     374                 :            : #ifdef _COMPILER_TSAN_ENABLED_
     375                 :            :     if (lastt->ctx.tsan_state != __tsan_get_current_fiber()) {
     376                 :            :         // Something went really wrong - don't even assume that we can
     377                 :            :         // use assert/abort which involve lots of signal handling that
     378                 :            :         // looks at the tsan state.
     379                 :            :         write(STDERR_FILENO, tsan_state_corruption, sizeof(tsan_state_corruption) - 1);
     380                 :            :         _exit(1);
     381                 :            :     }
     382                 :            : #endif
     383                 :            : 
     384                 :       3097 :     int killed = jl_atomic_load_relaxed(&lastt->_state) != JL_TASK_STATE_RUNNABLE;
     385   [ +  +  +  - ]:       3097 :     if (!t->started && !t->copy_stack) {
     386                 :            :         // may need to allocate the stack
     387         [ +  - ]:        148 :         if (t->stkbuf == NULL) {
     388                 :        148 :             t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t);
     389         [ -  + ]:        148 :             if (t->stkbuf == NULL) {
     390                 :            : #ifdef COPY_STACKS
     391                 :            :                 // fall back to stack copying if mmap fails
     392                 :          0 :                 t->copy_stack = 1;
     393                 :          0 :                 t->sticky = 1;
     394                 :          0 :                 t->bufsz = 0;
     395         [ #  # ]:          0 :                 if (always_copy_stacks)
     396                 :          0 :                     memcpy(&t->ctx.copy_ctx, &ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx));
     397                 :            :                 else
     398                 :          0 :                     memcpy(&t->ctx.ctx, &ptls->base_ctx, sizeof(t->ctx.ctx));
     399                 :            : #else
     400                 :            :                 jl_throw(jl_memory_exception);
     401                 :            : #endif
     402                 :            :             }
     403                 :            :         }
     404                 :            :     }
     405                 :            : 
     406         [ +  + ]:       3097 :     if (killed) {
     407                 :        130 :         *pt = NULL; // can't fail after here: clear the gc-root for the target task now
     408                 :        130 :         lastt->gcstack = NULL;
     409   [ +  -  +  - ]:        130 :         if (!lastt->copy_stack && lastt->stkbuf) {
     410                 :            :             // early free of stkbuf back to the pool
     411                 :        130 :             jl_release_task_stack(ptls, lastt);
     412                 :            :         }
     413                 :            :     }
     414                 :            :     else {
     415                 :            : #ifdef COPY_STACKS
     416         [ -  + ]:       2967 :         if (lastt->copy_stack) { // save the old copy-stack
     417                 :          0 :             save_stack(ptls, lastt, pt); // allocates (gc-safepoint, and can also fail)
     418         [ #  # ]:          0 :             if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0)) {
     419                 :          0 :                 sanitizer_finish_switch_fiber();
     420                 :            :                 // TODO: mutex unlock the thread we just switched from
     421                 :          0 :                 return;
     422                 :            :             }
     423                 :            :         }
     424                 :            :         else
     425                 :            : #endif
     426                 :       2967 :         *pt = NULL; // can't fail after here: clear the gc-root for the target task now
     427                 :            :     }
     428                 :            : 
     429                 :            :     // set up global state for new task and clear global state for old task
     430                 :       3097 :     t->ptls = ptls;
     431                 :       3097 :     jl_atomic_store_relaxed(&ptls->current_task, t);
     432                 :            :     JL_GC_PROMISE_ROOTED(t);
     433                 :       3097 :     jl_signal_fence();
     434                 :       3097 :     jl_set_pgcstack(&t->gcstack);
     435                 :       3097 :     jl_signal_fence();
     436                 :       3097 :     lastt->ptls = NULL;
     437                 :            : #ifdef MIGRATE_TASKS
     438                 :       3097 :     ptls->previous_task = lastt;
     439                 :            : #endif
     440                 :            : 
     441         [ +  + ]:       3097 :     if (t->started) {
     442                 :            : #ifdef COPY_STACKS
     443         [ -  + ]:       2949 :         if (t->copy_stack) {
     444   [ #  #  #  # ]:          0 :             if (!killed && !lastt->copy_stack)
     445                 :          0 :                 restore_stack2(t, ptls, lastt);
     446                 :            :             else {
     447                 :          0 :                 tsan_switch_to_copyctx(&t->ctx);
     448         [ #  # ]:          0 :                 if (killed)
     449                 :          0 :                     tsan_destroy_copyctx(ptls, &lastt->ctx);
     450                 :            : 
     451         [ #  # ]:          0 :                 if (lastt->copy_stack) {
     452                 :          0 :                     restore_stack(t, ptls, NULL); // (doesn't return)
     453                 :            :                 }
     454                 :            :                 else {
     455                 :          0 :                     restore_stack(t, ptls, (char*)1); // (doesn't return)
     456                 :            :                 }
     457                 :            :             }
     458                 :            :         }
     459                 :            :         else
     460                 :            : #endif
     461                 :            :         {
     462                 :       2949 :             sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
     463         [ +  + ]:       2949 :             if (killed) {
     464                 :        103 :                 tsan_switch_to_ctx(&t->ctx);
     465                 :        103 :                 tsan_destroy_ctx(ptls, &lastt->ctx);
     466                 :        103 :                 jl_set_fiber(&t->ctx); // (doesn't return)
     467                 :          0 :                 abort(); // unreachable
     468                 :            :             }
     469                 :            :             else {
     470         [ -  + ]:       2846 :                 if (lastt->copy_stack) {
     471                 :            :                     // Resume at the jl_setjmp earlier in this function,
     472                 :            :                     // don't do a full task swap
     473                 :          0 :                     tsan_switch_to_ctx(&t->ctx);
     474                 :          0 :                     jl_set_fiber(&t->ctx); // (doesn't return)
     475                 :            :                 }
     476                 :            :                 else {
     477                 :       2846 :                     jl_swap_fiber(&lastt->ctx, &t->ctx);
     478                 :            :                 }
     479                 :            :             }
     480                 :            :         }
     481                 :            :     }
     482                 :            :     else {
     483                 :        148 :         sanitizer_start_switch_fiber(t->stkbuf, t->bufsz);
     484   [ -  +  -  - ]:        148 :         if (t->copy_stack && always_copy_stacks) {
     485                 :          0 :             tsan_switch_to_ctx(&t->ctx);
     486         [ #  # ]:          0 :             if (killed) {
     487                 :          0 :                 tsan_destroy_ctx(ptls, &lastt->ctx);
     488                 :            :             }
     489                 :            : #ifdef COPY_STACKS
     490                 :            : #if defined(_OS_WINDOWS_)
     491                 :            :             jl_setcontext(&t->ctx.copy_ctx);
     492                 :            : #else
     493                 :          0 :             jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1);
     494                 :            : #endif
     495                 :            : #endif
     496                 :            :             abort(); // unreachable
     497                 :            :         }
     498                 :            :         else {
     499         [ +  + ]:        148 :             if (killed) {
     500                 :         27 :                 tsan_switch_to_ctx(&t->ctx);
     501                 :         27 :                 tsan_destroy_ctx(ptls, &lastt->ctx);
     502                 :         27 :                 jl_start_fiber_set(&t->ctx); // (doesn't return)
     503                 :          0 :                 abort();
     504                 :            :             }
     505         [ -  + ]:        121 :             else if (lastt->copy_stack) {
     506                 :            :                 // Resume at the jl_setjmp earlier in this function
     507                 :          0 :                 tsan_switch_to_ctx(&t->ctx);
     508                 :          0 :                 jl_start_fiber_set(&t->ctx); // (doesn't return)
     509                 :          0 :                 abort();
     510                 :            :             }
     511                 :            :             else {
     512                 :        121 :                 jl_start_fiber_swap(&lastt->ctx, &t->ctx);
     513                 :            :             }
     514                 :            :         }
     515                 :            :     }
     516                 :       2949 :     sanitizer_finish_switch_fiber();
     517                 :            : }
     518                 :            : 
     519                 :      11434 : JL_DLLEXPORT void jl_switch(void)
     520                 :            : {
     521                 :      11434 :     jl_task_t *ct = jl_current_task;
     522                 :      11434 :     jl_ptls_t ptls = ct->ptls;
     523                 :      11434 :     jl_task_t *t = ptls->next_task;
     524         [ +  + ]:      11434 :     if (t == ct) {
     525                 :       8337 :         return;
     526                 :            :     }
     527   [ +  +  -  + ]:       3097 :     if (t->started && t->stkbuf == NULL)
     528                 :          0 :         jl_error("attempt to switch to exited task");
     529         [ -  + ]:       3097 :     if (ptls->in_finalizer)
     530                 :          0 :         jl_error("task switch not allowed from inside gc finalizer");
     531         [ -  + ]:       3097 :     if (ptls->in_pure_callback)
     532                 :          0 :         jl_error("task switch not allowed from inside staged nor pure functions");
     533         [ -  + ]:       3097 :     if (!jl_set_task_tid(t, jl_atomic_load_relaxed(&ct->tid))) // manually yielding to a task
     534                 :          0 :         jl_error("cannot switch to task running on another thread");
     535                 :            : 
     536                 :            :     JL_PROBE_RT_PAUSE_TASK(ct);
     537                 :            : 
     538                 :            :     // Store old values on the stack and reset
     539                 :       3097 :     sig_atomic_t defer_signal = ptls->defer_signal;
     540                 :       3097 :     int8_t gc_state = jl_gc_unsafe_enter(ptls);
     541                 :       3097 :     int finalizers_inhibited = ptls->finalizers_inhibited;
     542                 :       3097 :     ptls->finalizers_inhibited = 0;
     543                 :            : 
     544                 :            : #ifdef ENABLE_TIMINGS
     545                 :            :     jl_timing_block_t *blk = ptls->timing_stack;
     546                 :            :     if (blk)
     547                 :            :         jl_timing_block_stop(blk);
     548                 :            :     ptls->timing_stack = NULL;
     549                 :            : #endif
     550                 :            : 
     551                 :       3097 :     ctx_switch(ct);
     552                 :            : 
     553                 :            : #ifdef MIGRATE_TASKS
     554                 :       2949 :     ptls = ct->ptls;
     555                 :       2949 :     t = ptls->previous_task;
     556                 :       2949 :     ptls->previous_task = NULL;
     557         [ -  + ]:       2949 :     assert(t != ct);
     558         [ -  + ]:       2949 :     assert(jl_atomic_load_relaxed(&t->tid) == ptls->tid);
     559   [ +  +  +  - ]:       2949 :     if (!t->sticky && !t->copy_stack)
     560                 :         22 :         jl_atomic_store_release(&t->tid, -1);
     561                 :            : #else
     562                 :            :     assert(ptls == ct->ptls);
     563                 :            : #endif
     564                 :            : 
     565                 :            :     // Pop old values back off the stack
     566   [ +  -  +  -  :       2949 :     assert(ct == jl_current_task &&
                   +  - ]
     567                 :            :            0 != ct->ptls &&
     568                 :            :            0 == ptls->finalizers_inhibited);
     569                 :       2949 :     ptls->finalizers_inhibited = finalizers_inhibited;
     570                 :            : 
     571                 :            : #ifdef ENABLE_TIMINGS
     572                 :            :     assert(ptls->timing_stack == NULL);
     573                 :            :     ptls->timing_stack = blk;
     574                 :            :     if (blk)
     575                 :            :         jl_timing_block_start(blk);
     576                 :            : #else
     577                 :            :     (void)ct;
     578                 :            : #endif
     579                 :            : 
     580                 :       2949 :     jl_gc_unsafe_leave(ptls, gc_state);
     581                 :       2949 :     sig_atomic_t other_defer_signal = ptls->defer_signal;
     582                 :       2949 :     ptls->defer_signal = defer_signal;
     583   [ -  +  -  - ]:       2949 :     if (other_defer_signal && !defer_signal)
     584                 :          0 :         jl_sigint_safepoint(ptls);
     585                 :            : 
     586                 :            :     JL_PROBE_RT_RUN_TASK(ct);
     587                 :            : }
     588                 :            : 
     589                 :          0 : JL_DLLEXPORT void jl_switchto(jl_task_t **pt)
     590                 :            : {
     591                 :          0 :     jl_set_next_task(*pt);
     592                 :          0 :     jl_switch();
     593                 :          0 : }
     594                 :            : 
     595                 :          0 : JL_DLLEXPORT JL_NORETURN void jl_no_exc_handler(jl_value_t *e)
     596                 :            : {
     597                 :            :     // NULL exception objects are used when rethrowing. we don't have a handler to process
     598                 :            :     // the exception stack, so at least report the exception at the top of the stack.
     599         [ #  # ]:          0 :     if (!e)
     600                 :          0 :         e = jl_current_exception();
     601                 :            : 
     602                 :          0 :     jl_printf((JL_STREAM*)STDERR_FILENO, "fatal: error thrown and no exception handler available.\n");
     603                 :          0 :     jl_static_show((JL_STREAM*)STDERR_FILENO, e);
     604                 :          0 :     jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
     605                 :          0 :     jlbacktrace(); // written to STDERR_FILENO
     606                 :          0 :     jl_exit(1);
     607                 :            : }
     608                 :            : 
     609                 :            : // yield to exception handler
     610                 :      12046 : static void JL_NORETURN throw_internal(jl_task_t *ct, jl_value_t *exception JL_MAYBE_UNROOTED)
     611                 :            : {
     612         [ -  + ]:      12046 :     assert(!jl_get_safe_restore());
     613                 :      12046 :     jl_ptls_t ptls = ct->ptls;
     614                 :      12046 :     ptls->io_wait = 0;
     615                 :      12046 :     JL_GC_PUSH1(&exception);
     616                 :      12046 :     jl_gc_unsafe_enter(ptls);
     617         [ +  + ]:      12046 :     if (exception) {
     618                 :            :         // The temporary ptls->bt_data is rooted by special purpose code in the
     619                 :            :         // GC. This exists only for the purpose of preserving bt_data until we
     620                 :            :         // set ptls->bt_size=0 below.
     621                 :       9000 :         jl_push_excstack(&ct->excstack, exception,
     622                 :       9000 :                           ptls->bt_data, ptls->bt_size);
     623                 :       9000 :         ptls->bt_size = 0;
     624                 :            :     }
     625   [ +  -  +  - ]:      12046 :     assert(ct->excstack && ct->excstack->top);
     626                 :      12046 :     jl_handler_t *eh = ct->eh;
     627         [ +  - ]:      12046 :     if (eh != NULL) {
     628                 :            : #ifdef ENABLE_TIMINGS
     629                 :            :         jl_timing_block_t *cur_block = ptls->timing_stack;
     630                 :            :         while (cur_block && eh->timing_stack != cur_block) {
     631                 :            :             cur_block = jl_pop_timing_block(cur_block);
     632                 :            :         }
     633                 :            :         assert(cur_block == eh->timing_stack);
     634                 :            : #endif
     635                 :      12046 :         jl_longjmp(eh->eh_ctx, 1);
     636                 :            :     }
     637                 :            :     else {
     638                 :          0 :         jl_no_exc_handler(exception);
     639                 :            :     }
     640                 :            :     assert(0);
     641                 :            : }
     642                 :            : 
     643                 :            : // record backtrace and raise an error
     644                 :       9000 : JL_DLLEXPORT void jl_throw(jl_value_t *e JL_MAYBE_UNROOTED)
     645                 :            : {
     646         [ -  + ]:       9000 :     assert(e != NULL);
     647                 :       9000 :     jl_jmp_buf *safe_restore = jl_get_safe_restore();
     648         [ -  + ]:       9000 :     if (safe_restore)
     649                 :          0 :         jl_longjmp(*safe_restore, 1);
     650                 :       9000 :     jl_task_t *ct = jl_get_current_task();
     651         [ -  + ]:       9000 :     if (ct == NULL) // During startup
     652                 :          0 :         jl_no_exc_handler(e);
     653                 :       9000 :     record_backtrace(ct->ptls, 1);
     654                 :       9000 :     throw_internal(ct, e);
     655                 :            : }
     656                 :            : 
     657                 :            : // rethrow with current excstack state
     658                 :       3046 : JL_DLLEXPORT void jl_rethrow(void)
     659                 :            : {
     660                 :       3046 :     jl_task_t *ct = jl_current_task;
     661                 :       3046 :     jl_excstack_t *excstack = ct->excstack;
     662   [ +  -  -  + ]:       3046 :     if (!excstack || excstack->top == 0)
     663                 :          0 :         jl_error("rethrow() not allowed outside a catch block");
     664                 :       3046 :     throw_internal(ct, NULL);
     665                 :            : }
     666                 :            : 
     667                 :            : // Special case throw for errors detected inside signal handlers.  This is not
     668                 :            : // (cannot be) called directly in the signal handler itself, but is returned to
     669                 :            : // after the signal handler exits.
     670                 :          0 : JL_DLLEXPORT void JL_NORETURN jl_sig_throw(void)
     671                 :            : {
     672                 :          0 : CFI_NORETURN
     673                 :          0 :     jl_jmp_buf *safe_restore = jl_get_safe_restore();
     674         [ #  # ]:          0 :     if (safe_restore)
     675                 :          0 :         jl_longjmp(*safe_restore, 1);
     676                 :          0 :     jl_task_t *ct = jl_current_task;
     677                 :          0 :     jl_ptls_t ptls = ct->ptls;
     678                 :          0 :     jl_value_t *e = ptls->sig_exception;
     679                 :          0 :     ptls->sig_exception = NULL;
     680                 :          0 :     throw_internal(ct, e);
     681                 :            : }
     682                 :            : 
     683                 :          0 : JL_DLLEXPORT void jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED)
     684                 :            : {
     685                 :            :     // TODO: Should uses of `rethrow(exc)` be replaced with a normal throw, now
     686                 :            :     // that exception stacks allow root cause analysis?
     687                 :          0 :     jl_task_t *ct = jl_current_task;
     688                 :          0 :     jl_excstack_t *excstack = ct->excstack;
     689   [ #  #  #  # ]:          0 :     if (!excstack || excstack->top == 0)
     690                 :          0 :         jl_error("rethrow(exc) not allowed outside a catch block");
     691                 :            :     // overwrite exception on top of stack. see jl_excstack_exception
     692                 :          0 :     jl_excstack_raw(excstack)[excstack->top-1].jlvalue = e;
     693                 :            :     JL_GC_PROMISE_ROOTED(e);
     694                 :          0 :     throw_internal(ct, NULL);
     695                 :            : }
     696                 :            : 
     697                 :            : /* This is xoshiro256++ 1.0, used for tasklocal random number generation in Julia.
     698                 :            :    This implementation is intended for embedders and internal use by the runtime, and is
     699                 :            :    based on the reference implementation at https://prng.di.unimi.it
     700                 :            : 
     701                 :            :    Credits go to David Blackman and Sebastiano Vigna for coming up with this PRNG.
     702                 :            :    They described xoshiro256++ in "Scrambled Linear Pseudorandom Number Generators",
     703                 :            :    ACM Trans. Math. Softw., 2021.
     704                 :            : 
     705                 :            :    There is a pure Julia implementation in stdlib that tends to be faster when used from
     706                 :            :    within Julia, due to inlining and more agressive architecture-specific optimizations.
     707                 :            : */
     708                 :       1388 : uint64_t jl_genrandom(uint64_t rngState[4]) JL_NOTSAFEPOINT
     709                 :            : {
     710                 :       1388 :     uint64_t s0 = rngState[0];
     711                 :       1388 :     uint64_t s1 = rngState[1];
     712                 :       1388 :     uint64_t s2 = rngState[2];
     713                 :       1388 :     uint64_t s3 = rngState[3];
     714                 :            : 
     715                 :       1388 :     uint64_t t = s1 << 17;
     716                 :       1388 :     uint64_t tmp = s0 + s3;
     717                 :       1388 :     uint64_t res = ((tmp << 23) | (tmp >> 41)) + s0;
     718                 :       1388 :     s2 ^= s0;
     719                 :       1388 :     s3 ^= s1;
     720                 :       1388 :     s1 ^= s2;
     721                 :       1388 :     s0 ^= s3;
     722                 :       1388 :     s2 ^= t;
     723                 :       1388 :     s3 = (s3 << 45) | (s3 >> 19);
     724                 :            : 
     725                 :       1388 :     rngState[0] = s0;
     726                 :       1388 :     rngState[1] = s1;
     727                 :       1388 :     rngState[2] = s2;
     728                 :       1388 :     rngState[3] = s3;
     729                 :       1388 :     return res;
     730                 :            : }
     731                 :            : 
     732                 :        347 : void jl_rng_split(uint64_t to[4], uint64_t from[4]) JL_NOTSAFEPOINT
     733                 :            : {
     734                 :            :     /* TODO: consider a less ad-hoc construction
     735                 :            :        Ideally we could just use the output of the random stream to seed the initial
     736                 :            :        state of the child. Out of an overabundance of caution we multiply with
     737                 :            :        effectively random coefficients, to break possible self-interactions.
     738                 :            : 
     739                 :            :        It is not the goal to mix bits -- we work under the assumption that the
     740                 :            :        source is well-seeded, and its output looks effectively random.
     741                 :            :        However, xoshiro has never been studied in the mode where we seed the
     742                 :            :        initial state with the output of another xoshiro instance.
     743                 :            : 
     744                 :            :        Constants have nothing up their sleeve:
     745                 :            :        0x02011ce34bce797f == hash(UInt(1))|0x01
     746                 :            :        0x5a94851fb48a6e05 == hash(UInt(2))|0x01
     747                 :            :        0x3688cf5d48899fa7 == hash(UInt(3))|0x01
     748                 :            :        0x867b4bb4c42e5661 == hash(UInt(4))|0x01
     749                 :            :     */
     750                 :        347 :     to[0] = 0x02011ce34bce797f * jl_genrandom(from);
     751                 :        347 :     to[1] = 0x5a94851fb48a6e05 * jl_genrandom(from);
     752                 :        347 :     to[2] = 0x3688cf5d48899fa7 * jl_genrandom(from);
     753                 :        347 :     to[3] = 0x867b4bb4c42e5661 * jl_genrandom(from);
     754                 :        347 : }
     755                 :            : 
     756                 :        176 : JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion_future, size_t ssize)
     757                 :            : {
     758                 :        176 :     jl_task_t *ct = jl_current_task;
     759                 :        176 :     jl_task_t *t = (jl_task_t*)jl_gc_alloc(ct->ptls, sizeof(jl_task_t), jl_task_type);
     760                 :            :     JL_PROBE_RT_NEW_TASK(ct, t);
     761                 :        176 :     t->copy_stack = 0;
     762         [ +  - ]:        176 :     if (ssize == 0) {
     763                 :            :         // stack size unspecified; use default
     764         [ -  + ]:        176 :         if (always_copy_stacks) {
     765                 :          0 :             t->copy_stack = 1;
     766                 :          0 :             t->bufsz = 0;
     767                 :            :         }
     768                 :            :         else {
     769                 :        176 :             t->bufsz = JL_STACK_SIZE;
     770                 :            :         }
     771                 :        176 :         t->stkbuf = NULL;
     772                 :            :     }
     773                 :            :     else {
     774                 :            :         // user requested dedicated stack of a certain size
     775         [ #  # ]:          0 :         if (ssize < MINSTKSZ)
     776                 :          0 :             ssize = MINSTKSZ;
     777                 :          0 :         t->bufsz = ssize;
     778                 :          0 :         t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t);
     779         [ #  # ]:          0 :         if (t->stkbuf == NULL)
     780                 :          0 :             jl_throw(jl_memory_exception);
     781                 :            :     }
     782                 :        176 :     t->next = jl_nothing;
     783                 :        176 :     t->queue = jl_nothing;
     784                 :        176 :     t->tls = jl_nothing;
     785                 :        176 :     jl_atomic_store_relaxed(&t->_state, JL_TASK_STATE_RUNNABLE);
     786                 :        176 :     t->start = start;
     787                 :        176 :     t->result = jl_nothing;
     788                 :        176 :     t->donenotify = completion_future;
     789                 :        176 :     jl_atomic_store_relaxed(&t->_isexception, 0);
     790                 :            :     // Inherit logger state from parent task
     791                 :        176 :     t->logstate = ct->logstate;
     792                 :            :     // Fork task-local random state from parent
     793                 :        176 :     jl_rng_split(t->rngState, ct->rngState);
     794                 :            :     // there is no active exception handler available on this stack yet
     795                 :        176 :     t->eh = NULL;
     796                 :        176 :     t->sticky = 1;
     797                 :        176 :     t->gcstack = NULL;
     798                 :        176 :     t->excstack = NULL;
     799                 :        176 :     t->started = 0;
     800                 :        176 :     t->priority = 0;
     801         [ -  + ]:        176 :     jl_atomic_store_relaxed(&t->tid, t->copy_stack ? jl_atomic_load_relaxed(&ct->tid) : -1); // copy_stacks are always pinned since they can't be moved
     802                 :        176 :     t->threadpoolid = ct->threadpoolid;
     803                 :        176 :     t->ptls = NULL;
     804                 :        176 :     t->world_age = ct->world_age;
     805                 :            : 
     806                 :            : #ifdef COPY_STACKS
     807         [ +  - ]:        176 :     if (!t->copy_stack) {
     808                 :            : #if defined(JL_DEBUG_BUILD)
     809                 :        176 :         memset(&t->ctx, 0, sizeof(t->ctx));
     810                 :            : #endif
     811                 :            :     }
     812                 :            :     else {
     813         [ #  # ]:          0 :         if (always_copy_stacks)
     814                 :          0 :             memcpy(&t->ctx.copy_ctx, &ct->ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx));
     815                 :            :         else
     816                 :          0 :             memcpy(&t->ctx.ctx, &ct->ptls->base_ctx, sizeof(t->ctx.ctx));
     817                 :            :     }
     818                 :            : #endif
     819                 :            : #ifdef _COMPILER_TSAN_ENABLED_
     820                 :            :     t->ctx.tsan_state = __tsan_create_fiber(0);
     821                 :            : #endif
     822                 :        176 :     return t;
     823                 :            : }
     824                 :            : 
     825                 :            : // a version of jl_current_task safe for unmanaged threads
     826                 :     127746 : JL_DLLEXPORT jl_task_t *jl_get_current_task(void)
     827                 :            : {
     828                 :     127746 :     jl_gcframe_t **pgcstack = jl_get_pgcstack();
     829         [ +  - ]:     127746 :     return pgcstack == NULL ? NULL : container_of(pgcstack, jl_task_t, gcstack);
     830                 :            : }
     831                 :            : 
     832                 :            : 
     833                 :            : #ifdef JL_HAVE_ASYNCIFY
     834                 :            : JL_DLLEXPORT jl_ucontext_t *task_ctx_ptr(jl_task_t *t)
     835                 :            : {
     836                 :            :     return &t->ctx.ctx;
     837                 :            : }
     838                 :            : 
     839                 :            : JL_DLLEXPORT jl_value_t *jl_get_root_task(void)
     840                 :            : {
     841                 :            :     jl_task_t *ct = jl_current_task;
     842                 :            :     return (jl_value_t*)ct->ptls->root_task;
     843                 :            : }
     844                 :            : 
     845                 :            : JL_DLLEXPORT void jl_task_wait()
     846                 :            : {
     847                 :            :     static jl_function_t *wait_func = NULL;
     848                 :            :     if (!wait_func) {
     849                 :            :         wait_func = (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("wait"));
     850                 :            :     }
     851                 :            :     jl_task_t *ct = jl_current_task;
     852                 :            :     size_t last_age = ct->world_age;
     853                 :            :     ct->world_age = jl_get_world_counter();
     854                 :            :     jl_apply(&wait_func, 1);
     855                 :            :     ct->world_age = last_age;
     856                 :            : }
     857                 :            : 
     858                 :            : JL_DLLEXPORT void jl_schedule_task(jl_task_t *task)
     859                 :            : {
     860                 :            :     static jl_function_t *sched_func = NULL;
     861                 :            :     if (!sched_func) {
     862                 :            :         sched_func = (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("schedule"));
     863                 :            :     }
     864                 :            :     jl_task_t *ct = jl_current_task;
     865                 :            :     size_t last_age = ct->world_age;
     866                 :            :     ct->world_age = jl_get_world_counter();
     867                 :            :     jl_value_t *args[] = {(jl_value_t*)sched_func, (jl_value_t*)task};
     868                 :            :     jl_apply(args, 2);
     869                 :            :     ct->world_age = last_age;
     870                 :            : }
     871                 :            : #endif
     872                 :            : 
     873                 :            : // Do one-time initializations for task system
     874                 :         15 : void jl_init_tasks(void) JL_GC_DISABLED
     875                 :            : {
     876                 :         15 :     char *acs = getenv("JULIA_COPY_STACKS");
     877         [ -  + ]:         15 :     if (acs) {
     878   [ #  #  #  # ]:          0 :         if (!strcmp(acs, "1") || !strcmp(acs, "yes"))
     879                 :          0 :             always_copy_stacks = 1;
     880   [ #  #  #  # ]:          0 :         else if (!strcmp(acs, "0") || !strcmp(acs, "no"))
     881                 :          0 :             always_copy_stacks = 0;
     882                 :            :         else {
     883                 :          0 :             jl_safe_printf("invalid JULIA_COPY_STACKS value: %s\n", acs);
     884                 :          0 :             exit(1);
     885                 :            :         }
     886                 :            :     }
     887                 :            : #ifndef COPY_STACKS
     888                 :            :     if (always_copy_stacks) {
     889                 :            :         jl_safe_printf("Julia built without COPY_STACKS support");
     890                 :            :         exit(1);
     891                 :            :     }
     892                 :            : #endif
     893                 :         15 : }
     894                 :            : 
     895                 :        148 : STATIC_OR_JS void NOINLINE JL_NORETURN start_task(void)
     896                 :            : {
     897                 :        148 : CFI_NORETURN
     898                 :            :     // this runs the first time we switch to a task
     899                 :        148 :     sanitizer_finish_switch_fiber();
     900                 :            : #ifdef __clang_gcanalyzer__
     901                 :            :     jl_task_t *ct = jl_get_current_task();
     902                 :            : #else
     903                 :        148 :     jl_task_t *ct = jl_current_task;
     904                 :            : #endif
     905                 :        148 :     jl_ptls_t ptls = ct->ptls;
     906                 :            :     jl_value_t *res;
     907         [ -  + ]:        148 :     assert(ptls->finalizers_inhibited == 0);
     908                 :            : 
     909                 :            : #ifdef MIGRATE_TASKS
     910                 :        148 :     jl_task_t *pt = ptls->previous_task;
     911                 :        148 :     ptls->previous_task = NULL;
     912   [ +  +  +  - ]:        148 :     if (!pt->sticky && !pt->copy_stack)
     913                 :         12 :         jl_atomic_store_release(&pt->tid, -1);
     914                 :            : #endif
     915                 :            : 
     916                 :        148 :     ct->started = 1;
     917                 :            :     JL_PROBE_RT_START_TASK(ct);
     918         [ -  + ]:        148 :     if (jl_atomic_load_relaxed(&ct->_isexception)) {
     919                 :          0 :         record_backtrace(ptls, 0);
     920                 :          0 :         jl_push_excstack(&ct->excstack, ct->result,
     921                 :          0 :                          ptls->bt_data, ptls->bt_size);
     922                 :          0 :         res = ct->result;
     923                 :            :     }
     924                 :            :     else {
     925   [ +  -  +  + ]:        278 :         JL_TRY {
     926         [ -  + ]:        148 :             if (ptls->defer_signal) {
     927                 :          0 :                 ptls->defer_signal = 0;
     928                 :          0 :                 jl_sigint_safepoint(ptls);
     929                 :            :             }
     930                 :            :             JL_TIMING(ROOT);
     931                 :        148 :             res = jl_apply(&ct->start, 1);
     932                 :            :         }
     933         [ #  # ]:          0 :         JL_CATCH {
     934                 :          0 :             res = jl_current_exception();
     935                 :          0 :             jl_atomic_store_relaxed(&ct->_isexception, 1);
     936                 :          0 :             goto skip_pop_exception;
     937                 :            :         }
     938                 :        130 : skip_pop_exception:;
     939                 :            :     }
     940                 :        130 :     ct->result = res;
     941                 :        130 :     jl_gc_wb(ct, ct->result);
     942                 :        130 :     jl_finish_task(ct);
     943                 :            :     jl_gc_debug_critical_error();
     944                 :            :     abort();
     945                 :            : }
     946                 :            : 
     947                 :            : 
     948                 :            : #if defined(JL_HAVE_UCONTEXT)
     949                 :            : #ifdef _OS_WINDOWS_
     950                 :            : #define setcontext jl_setcontext
     951                 :            : #define swapcontext jl_swapcontext
     952                 :            : #define makecontext jl_makecontext
     953                 :            : #endif
     954                 :            : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT
     955                 :            : {
     956                 :            : #ifndef _OS_WINDOWS_
     957                 :            :     int r = getcontext(t);
     958                 :            :     if (r != 0)
     959                 :            :         jl_error("getcontext failed");
     960                 :            : #endif
     961                 :            :     void *stk = jl_malloc_stack(ssize, owner);
     962                 :            :     if (stk == NULL)
     963                 :            :         return NULL;
     964                 :            :     t->uc_stack.ss_sp = stk;
     965                 :            :     t->uc_stack.ss_size = *ssize;
     966                 :            : #ifdef _OS_WINDOWS_
     967                 :            :     makecontext(t, &start_task);
     968                 :            : #else
     969                 :            :     t->uc_link = NULL;
     970                 :            :     makecontext(t, &start_task, 0);
     971                 :            : #endif
     972                 :            :     return (char*)stk;
     973                 :            : }
     974                 :            : static void jl_start_fiber_set(jl_ucontext_t *t)
     975                 :            : {
     976                 :            :     setcontext(&t->ctx);
     977                 :            : }
     978                 :            : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
     979                 :            : {
     980                 :            :     assert(lastt);
     981                 :            :     tsan_switch_to_ctx(t);
     982                 :            :     swapcontext(&lastt->ctx, &t->ctx);
     983                 :            : }
     984                 :            : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
     985                 :            : {
     986                 :            :     tsan_switch_to_ctx(t);
     987                 :            :     swapcontext(&lastt->ctx, &t->ctx);
     988                 :            : }
     989                 :            : static void jl_set_fiber(jl_ucontext_t *t)
     990                 :            : {
     991                 :            :     setcontext(&t->ctx);
     992                 :            : }
     993                 :            : #endif
     994                 :            : 
     995                 :            : #if defined(JL_HAVE_UNW_CONTEXT) || defined(JL_HAVE_ASM)
     996                 :        163 : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner)
     997                 :            : {
     998                 :        163 :     char *stkbuf = (char*)jl_malloc_stack(ssize, owner);
     999         [ -  + ]:        163 :     if (stkbuf == NULL)
    1000                 :          0 :         return NULL;
    1001                 :            : #ifndef __clang_gcanalyzer__
    1002                 :        163 :     ((char**)t)[0] = stkbuf; // stash the stack pointer somewhere for start_fiber
    1003                 :        163 :     ((size_t*)t)[1] = *ssize; // stash the stack size somewhere for start_fiber
    1004                 :            : #endif
    1005                 :        163 :     return stkbuf;
    1006                 :            : }
    1007                 :            : #endif
    1008                 :            : 
    1009                 :            : #if defined(JL_HAVE_UNW_CONTEXT)
    1010                 :            : static inline void jl_unw_swapcontext(unw_context_t *old, unw_cursor_t *c)
    1011                 :            : {
    1012                 :            :     volatile int returns = 0;
    1013                 :            :     int r = unw_getcontext(old);
    1014                 :            :     if (++returns == 2) // r is garbage after the first return
    1015                 :            :         return;
    1016                 :            :     if (r != 0 || returns != 1)
    1017                 :            :         abort();
    1018                 :            :     unw_resume(c);
    1019                 :            : }
    1020                 :            : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
    1021                 :            : {
    1022                 :            :     unw_cursor_t c;
    1023                 :            :     int r = unw_init_local(&c, &t->ctx);
    1024                 :            :     if (r < 0)
    1025                 :            :         abort();
    1026                 :            :     jl_unw_swapcontext(&lastt->ctx, &c);
    1027                 :            : }
    1028                 :            : static void jl_set_fiber(jl_ucontext_t *t)
    1029                 :            : {
    1030                 :            :     unw_cursor_t c;
    1031                 :            :     int r = unw_init_local(&c, &t->ctx);
    1032                 :            :     if (r < 0)
    1033                 :            :         abort();
    1034                 :            :     unw_resume(&c);
    1035                 :            : }
    1036                 :            : #elif defined(JL_HAVE_ASM)
    1037                 :       2846 : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
    1038                 :            : {
    1039         [ +  + ]:       2846 :     if (jl_setjmp(lastt->ctx.uc_mcontext, 0))
    1040                 :       2836 :         return;
    1041                 :       2846 :     tsan_switch_to_ctx(t);
    1042                 :       2846 :     jl_set_fiber(t); // doesn't return
    1043                 :            : }
    1044                 :       2949 : static void jl_set_fiber(jl_ucontext_t *t)
    1045                 :            : {
    1046                 :       2949 :     jl_longjmp(t->ctx.uc_mcontext, 1);
    1047                 :            : }
    1048                 :            : #endif
    1049                 :            : 
    1050                 :            : #if defined(JL_HAVE_UNW_CONTEXT) && !defined(JL_HAVE_ASM)
    1051                 :            : #if defined(_CPU_X86_) || defined(_CPU_X86_64_)
    1052                 :            : #define PUSH_RET(ctx, stk) \
    1053                 :            :     do { \
    1054                 :            :         stk -= sizeof(uintptr_t); \
    1055                 :            :         *(uintptr_t*)stk = 0; /* push null RIP/EIP onto the stack */ \
    1056                 :            :     } while (0)
    1057                 :            : #elif defined(_CPU_ARM_)
    1058                 :            : #define PUSH_RET(ctx, stk) \
    1059                 :            :     if (unw_set_reg(ctx, UNW_ARM_R14, 0)) /* put NULL into the LR */ \
    1060                 :            :         abort();
    1061                 :            : #else
    1062                 :            : #error please define how to simulate a CALL on this platform
    1063                 :            : #endif
    1064                 :            : static void jl_start_fiber_set(jl_ucontext_t *t)
    1065                 :            : {
    1066                 :            :     unw_cursor_t c;
    1067                 :            :     char *stk = ((char**)&t->ctx)[0];
    1068                 :            :     size_t ssize = ((size_t*)&t->ctx)[1];
    1069                 :            :     uintptr_t fn = (uintptr_t)&start_task;
    1070                 :            :     stk += ssize;
    1071                 :            :     int r = unw_getcontext(&t->ctx);
    1072                 :            :     if (r)
    1073                 :            :         abort();
    1074                 :            :     if (unw_init_local(&c, &t->ctx))
    1075                 :            :         abort();
    1076                 :            :     PUSH_RET(&c, stk);
    1077                 :            : #if defined __linux__
    1078                 :            : #error savannah nongnu libunwind is incapable of setting UNW_REG_SP, as required
    1079                 :            : #endif
    1080                 :            :     if (unw_set_reg(&c, UNW_REG_SP, (uintptr_t)stk))
    1081                 :            :         abort();
    1082                 :            :     if (unw_set_reg(&c, UNW_REG_IP, fn))
    1083                 :            :         abort();
    1084                 :            :     unw_resume(&c); // (doesn't return)
    1085                 :            : }
    1086                 :            : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
    1087                 :            : {
    1088                 :            :     assert(lastt);
    1089                 :            :     unw_cursor_t c;
    1090                 :            :     char *stk = ((char**)&t->ctx)[0];
    1091                 :            :     size_t ssize = ((size_t*)&t->ctx)[1];
    1092                 :            :     uintptr_t fn = (uintptr_t)&start_task;
    1093                 :            :     stk += ssize;
    1094                 :            :     volatile int returns = 0;
    1095                 :            :     int r = unw_getcontext(&lastt->ctx);
    1096                 :            :     if (++returns == 2) // r is garbage after the first return
    1097                 :            :         return;
    1098                 :            :     if (r != 0 || returns != 1)
    1099                 :            :         abort();
    1100                 :            :     r = unw_getcontext(&t->ctx);
    1101                 :            :     if (r != 0)
    1102                 :            :         abort();
    1103                 :            :     if (unw_init_local(&c, &t->ctx))
    1104                 :            :         abort();
    1105                 :            :     PUSH_RET(&c, stk);
    1106                 :            :     if (unw_set_reg(&c, UNW_REG_SP, (uintptr_t)stk))
    1107                 :            :         abort();
    1108                 :            :     if (unw_set_reg(&c, UNW_REG_IP, fn))
    1109                 :            :         abort();
    1110                 :            :     jl_unw_swapcontext(&lastt->ctx, &c);
    1111                 :            : }
    1112                 :            : #endif
    1113                 :            : 
    1114                 :            : #if defined(JL_HAVE_ASM)
    1115                 :        121 : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
    1116                 :            : {
    1117         [ -  + ]:        121 :     assert(lastt);
    1118                 :            : #ifdef JL_HAVE_UNW_CONTEXT
    1119                 :            :     volatile int returns = 0;
    1120                 :            :     int r = unw_getcontext(&lastt->ctx);
    1121                 :            :     if (++returns == 2) // r is garbage after the first return
    1122                 :            :         return;
    1123                 :            :     if (r != 0 || returns != 1)
    1124                 :            :         abort();
    1125                 :            : #else
    1126         [ +  + ]:        121 :     if (jl_setjmp(lastt->ctx.uc_mcontext, 0))
    1127                 :        113 :         return;
    1128                 :            : #endif
    1129                 :        121 :     tsan_switch_to_ctx(t);
    1130                 :        121 :     jl_start_fiber_set(t); // doesn't return
    1131                 :            : }
    1132                 :        148 : static void jl_start_fiber_set(jl_ucontext_t *t)
    1133                 :            : {
    1134                 :        148 :     char *stk = ((char**)&t->ctx)[0];
    1135                 :        148 :     size_t ssize = ((size_t*)&t->ctx)[1];
    1136                 :        148 :     uintptr_t fn = (uintptr_t)&start_task;
    1137                 :        148 :     stk += ssize;
    1138                 :            : #ifdef _CPU_X86_64_
    1139                 :        148 :     asm volatile (
    1140                 :            :         " movq %0, %%rsp;\n"
    1141                 :            :         " movq %1, %%rax;\n"
    1142                 :            :         " xorq %%rbp, %%rbp;\n"
    1143                 :            :         " push %%rbp;\n" // instead of RSP
    1144                 :            :         " jmpq *%%rax;\n" // call `fn` with fake stack frame
    1145                 :            :         " ud2"
    1146                 :            :         : : "r"(stk), "r"(fn) : "memory" );
    1147                 :            : #elif defined(_CPU_X86_)
    1148                 :            :     asm volatile (
    1149                 :            :         " movl %0, %%esp;\n"
    1150                 :            :         " movl %1, %%eax;\n"
    1151                 :            :         " xorl %%ebp, %%ebp;\n"
    1152                 :            :         " push %%ebp;\n" // instead of ESP
    1153                 :            :         " jmpl *%%eax;\n" // call `fn` with fake stack frame
    1154                 :            :         " ud2"
    1155                 :            :         : : "r"(stk), "r"(fn) : "memory" );
    1156                 :            : #elif defined(_CPU_AARCH64_)
    1157                 :            :     asm volatile(
    1158                 :            :         " mov sp, %0;\n"
    1159                 :            :         " mov x29, xzr;\n" // Clear link register (x29) and frame pointer
    1160                 :            :         " mov x30, xzr;\n" // (x30) to terminate unwinder.
    1161                 :            :         " br %1;\n" // call `fn` with fake stack frame
    1162                 :            :         " brk #0x1" // abort
    1163                 :            :         : : "r" (stk), "r"(fn) : "memory" );
    1164                 :            : #elif defined(_CPU_ARM_)
    1165                 :            :     // A "i" constraint on `&start_task` works only on clang and not on GCC.
    1166                 :            :     asm(" mov sp, %0;\n"
    1167                 :            :         " mov lr, #0;\n" // Clear link register (lr) and frame pointer
    1168                 :            :         " mov fp, #0;\n" // (fp) to terminate unwinder.
    1169                 :            :         " bx %1;\n" // call `fn` with fake stack frame.  While `bx` can change
    1170                 :            :                     // the processor mode to thumb, this will never happen
    1171                 :            :                     // because all our addresses are word-aligned.
    1172                 :            :         " udf #0" // abort
    1173                 :            :         : : "r" (stk), "r"(fn) : "memory" );
    1174                 :            : #elif defined(_CPU_PPC64_)
    1175                 :            :     // N.B.: There is two iterations of the PPC64 ABI.
    1176                 :            :     // v2 is current and used here. Make sure you have the
    1177                 :            :     // correct version of the ABI reference when working on this code.
    1178                 :            :     asm volatile(
    1179                 :            :         // Move stack (-0x30 for initial stack frame) to stack pointer
    1180                 :            :         " addi 1, %0, -0x30;\n"
    1181                 :            :         // Build stack frame
    1182                 :            :         // Skip local variable save area
    1183                 :            :         " std 2, 0x28(1);\n" // Save TOC
    1184                 :            :         // Clear link editor/compiler words
    1185                 :            :         " std 0, 0x20(1);\n"
    1186                 :            :         " std 0, 0x18(1);\n"
    1187                 :            :         // Clear LR/CR save area
    1188                 :            :         " std 0, 0x10(1);\n"
    1189                 :            :         " std 0, 0x8(1);\n"
    1190                 :            :         " std 0, 0x0(1); \n" // Clear back link to terminate unwinder
    1191                 :            :         " mtlr 0; \n"        // Clear link register
    1192                 :            :         " mr 12, %1; \n"     // Set up target global entry point
    1193                 :            :         " mtctr 12; \n"      // Move jump target to counter register
    1194                 :            :         " bctr; \n"          // branch to counter (lr update disabled)
    1195                 :            :         " trap; \n"
    1196                 :            :         : : "r"(stk), "r"(fn) : "memory");
    1197                 :            : #else
    1198                 :            : #error JL_HAVE_ASM defined but not implemented for this CPU type
    1199                 :            : #endif
    1200                 :          0 :     __builtin_unreachable();
    1201                 :            : }
    1202                 :            : #endif
    1203                 :            : 
    1204                 :            : #if defined(JL_HAVE_SIGALTSTACK)
    1205                 :            : #if defined(_COMPILER_TSAN_ENABLED_)
    1206                 :            : #error TSAN support not currently implemented for this tasking model
    1207                 :            : #endif
    1208                 :            : 
    1209                 :            : static void start_basefiber(int sig)
    1210                 :            : {
    1211                 :            :     jl_ptls_t ptls = jl_current_task->ptls;
    1212                 :            :     if (jl_setjmp(ptls->base_ctx.uc_mcontext, 0))
    1213                 :            :         start_task(); // sanitizer_finish_switch_fiber is part of start_task
    1214                 :            : }
    1215                 :            : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner)
    1216                 :            : {
    1217                 :            :     stack_t uc_stack, osigstk;
    1218                 :            :     struct sigaction sa, osa;
    1219                 :            :     sigset_t set, oset;
    1220                 :            :     void *stk = jl_malloc_stack(ssize, owner);
    1221                 :            :     if (stk == NULL)
    1222                 :            :         return NULL;
    1223                 :            :     // setup
    1224                 :            :     jl_ptls_t ptls = jl_current_task->ptls;
    1225                 :            :     _jl_ucontext_t base_ctx;
    1226                 :            :     memcpy(&base_ctx, &ptls->base_ctx, sizeof(base_ctx));
    1227                 :            :     sigfillset(&set);
    1228                 :            :     if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) {
    1229                 :            :        jl_free_stack(stk, *ssize);
    1230                 :            :        jl_error("sigprocmask failed");
    1231                 :            :     }
    1232                 :            :     uc_stack.ss_sp = stk;
    1233                 :            :     uc_stack.ss_size = *ssize;
    1234                 :            :     uc_stack.ss_flags = 0;
    1235                 :            :     if (sigaltstack(&uc_stack, &osigstk) != 0) {
    1236                 :            :        jl_free_stack(stk, *ssize);
    1237                 :            :        jl_error("sigaltstack failed");
    1238                 :            :     }
    1239                 :            :     memset(&sa, 0, sizeof(sa));
    1240                 :            :     sigemptyset(&sa.sa_mask);
    1241                 :            :     sa.sa_handler = start_basefiber;
    1242                 :            :     sa.sa_flags = SA_ONSTACK;
    1243                 :            :     if (sigaction(SIGUSR2, &sa, &osa) != 0) {
    1244                 :            :        jl_free_stack(stk, *ssize);
    1245                 :            :        jl_error("sigaction failed");
    1246                 :            :     }
    1247                 :            :     // emit signal
    1248                 :            :     pthread_kill(pthread_self(), SIGUSR2); // initializes jl_basectx
    1249                 :            :     sigdelset(&set, SIGUSR2);
    1250                 :            :     sigsuspend(&set);
    1251                 :            :     // cleanup
    1252                 :            :     if (sigaction(SIGUSR2, &osa, NULL) != 0) {
    1253                 :            :        jl_free_stack(stk, *ssize);
    1254                 :            :        jl_error("sigaction failed");
    1255                 :            :     }
    1256                 :            :     if (osigstk.ss_size < MINSTKSZ && (osigstk.ss_flags | SS_DISABLE))
    1257                 :            :        osigstk.ss_size = MINSTKSZ;
    1258                 :            :     if (sigaltstack(&osigstk, NULL) != 0) {
    1259                 :            :        jl_free_stack(stk, *ssize);
    1260                 :            :        jl_error("sigaltstack failed");
    1261                 :            :     }
    1262                 :            :     if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) {
    1263                 :            :        jl_free_stack(stk, *ssize);
    1264                 :            :        jl_error("sigprocmask failed");
    1265                 :            :     }
    1266                 :            :     if (&ptls->base_ctx != t) {
    1267                 :            :         memcpy(&t, &ptls->base_ctx, sizeof(base_ctx));
    1268                 :            :         memcpy(&ptls->base_ctx, &base_ctx, sizeof(base_ctx)); // restore COPY_STACKS context
    1269                 :            :     }
    1270                 :            :     return (char*)stk;
    1271                 :            : }
    1272                 :            : static void jl_start_fiber_set(jl_ucontext_t *t) {
    1273                 :            :     jl_longjmp(t->ctx.uc_mcontext, 1); // (doesn't return)
    1274                 :            : }
    1275                 :            : static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t)
    1276                 :            : {
    1277                 :            :     assert(lastt);
    1278                 :            :     if (lastt && jl_setjmp(lastt->ctx.uc_mcontext, 0))
    1279                 :            :         return;
    1280                 :            :     tsan_switch_to_ctx(t);
    1281                 :            :     jl_start_fiber_set(t);
    1282                 :            : }
    1283                 :            : static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t)
    1284                 :            : {
    1285                 :            :     if (jl_setjmp(lastt->ctx.uc_mcontext, 0))
    1286                 :            :         return;
    1287                 :            :     tsan_switch_to_ctx(t);
    1288                 :            :     jl_start_fiber_set(t); // doesn't return
    1289                 :            : }
    1290                 :            : static void jl_set_fiber(jl_ucontext_t *t)
    1291                 :            : {
    1292                 :            :     jl_longjmp(t->ctx.uc_mcontext, 1);
    1293                 :            : }
    1294                 :            : #endif
    1295                 :            : 
    1296                 :            : #if defined(JL_HAVE_ASYNCIFY)
    1297                 :            : #if defined(_COMPILER_TSAN_ENABLED_)
    1298                 :            : #error TSAN support not currently implemented for this tasking model
    1299                 :            : #endif
    1300                 :            : 
    1301                 :            : static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT
    1302                 :            : {
    1303                 :            :     void *stk = jl_malloc_stack(ssize, owner);
    1304                 :            :     if (stk == NULL)
    1305                 :            :         return NULL;
    1306                 :            :     t->stackbottom = stk;
    1307                 :            :     t->stacktop = ((char*)stk) + *ssize;
    1308                 :            :     return (char*)stk;
    1309                 :            : }
    1310                 :            : // jl_*_fiber implemented in js
    1311                 :            : #endif
    1312                 :            : 
    1313                 :            : // Initialize a root task using the given stack.
    1314                 :         15 : jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi)
    1315                 :            : {
    1316         [ -  + ]:         15 :     assert(ptls->root_task == NULL);
    1317                 :            :     // We need `gcstack` in `Task` to allocate Julia objects; *including* the `Task` type.
    1318                 :            :     // However, to allocate a `Task` via `jl_gc_alloc` as done in `jl_init_root_task`,
    1319                 :            :     // we need the `Task` type itself. We use stack-allocated "raw" `jl_task_t` struct to
    1320                 :            :     // workaround this chicken-and-egg problem. Note that this relies on GC to be turned
    1321                 :            :     // off as GC fails because we don't/can't allocate the type tag.
    1322                 :            :     struct {
    1323                 :            :         jl_value_t *type;
    1324                 :            :         jl_task_t value;
    1325                 :         15 :     } bootstrap_task = {0};
    1326                 :         15 :     jl_set_pgcstack(&bootstrap_task.value.gcstack);
    1327                 :         15 :     bootstrap_task.value.ptls = ptls;
    1328         [ +  - ]:         15 :     if (jl_nothing == NULL) // make a placeholder
    1329                 :         15 :         jl_nothing = jl_gc_permobj(0, jl_nothing_type);
    1330                 :         15 :     jl_task_t *ct = (jl_task_t*)jl_gc_alloc(ptls, sizeof(jl_task_t), jl_task_type);
    1331                 :         15 :     memset(ct, 0, sizeof(jl_task_t));
    1332                 :         15 :     void *stack = stack_lo;
    1333                 :         15 :     size_t ssize = (char*)stack_hi - (char*)stack_lo;
    1334                 :            : #ifndef _OS_WINDOWS_
    1335         [ +  - ]:         15 :     if (ptls->tid == 0) {
    1336                 :         15 :         stack = (void*)((char*)stack - ROOT_TASK_STACK_ADJUSTMENT); // offset our guess of the address of the bottom of stack to cover the guard pages too
    1337                 :         15 :         ssize += ROOT_TASK_STACK_ADJUSTMENT; // sizeof stack is known exactly, but not where we are in that stack
    1338                 :            :     }
    1339                 :            : #endif
    1340         [ -  + ]:         15 :     if (always_copy_stacks) {
    1341                 :          0 :         ct->copy_stack = 1;
    1342                 :          0 :         ct->stkbuf = NULL;
    1343                 :          0 :         ct->bufsz = 0;
    1344                 :            :     }
    1345                 :            :     else {
    1346                 :         15 :         ct->copy_stack = 0;
    1347                 :         15 :         ct->stkbuf = stack;
    1348                 :         15 :         ct->bufsz = ssize;
    1349                 :            :     }
    1350                 :         15 :     ct->started = 1;
    1351                 :         15 :     ct->next = jl_nothing;
    1352                 :         15 :     ct->queue = jl_nothing;
    1353                 :         15 :     ct->tls = jl_nothing;
    1354                 :         15 :     jl_atomic_store_relaxed(&ct->_state, JL_TASK_STATE_RUNNABLE);
    1355                 :         15 :     ct->start = NULL;
    1356                 :         15 :     ct->result = jl_nothing;
    1357                 :         15 :     ct->donenotify = jl_nothing;
    1358                 :         15 :     jl_atomic_store_relaxed(&ct->_isexception, 0);
    1359                 :         15 :     ct->logstate = jl_nothing;
    1360                 :         15 :     ct->eh = NULL;
    1361                 :         15 :     ct->gcstack = NULL;
    1362                 :         15 :     ct->excstack = NULL;
    1363                 :         15 :     jl_atomic_store_relaxed(&ct->tid, ptls->tid);
    1364                 :         15 :     ct->threadpoolid = jl_threadpoolid(ptls->tid);
    1365                 :         15 :     ct->sticky = 1;
    1366                 :         15 :     ct->ptls = ptls;
    1367                 :         15 :     ct->world_age = 1; // OK to run Julia code on this task
    1368                 :         15 :     ptls->root_task = ct;
    1369                 :         15 :     jl_atomic_store_relaxed(&ptls->current_task, ct);
    1370                 :            :     JL_GC_PROMISE_ROOTED(ct);
    1371                 :         15 :     jl_set_pgcstack(&ct->gcstack);
    1372         [ -  + ]:         15 :     assert(jl_current_task == ct);
    1373                 :            : 
    1374                 :            : #ifdef _COMPILER_TSAN_ENABLED_
    1375                 :            :     ct->ctx.tsan_state = __tsan_get_current_fiber();
    1376                 :            : #endif
    1377                 :            : 
    1378                 :            : #ifdef COPY_STACKS
    1379                 :            :     // initialize the base_ctx from which all future copy_stacks will be copies
    1380         [ -  + ]:         15 :     if (always_copy_stacks) {
    1381                 :            :         // when this is set, we will attempt to corrupt the process stack to switch tasks,
    1382                 :            :         // although this is unreliable, and thus not recommended
    1383                 :          0 :         ptls->stackbase = stack_hi;
    1384                 :          0 :         ptls->stacksize = ssize;
    1385                 :            : #ifdef _OS_WINDOWS_
    1386                 :            :         ptls->copy_stack_ctx.uc_stack.ss_sp = stack_hi;
    1387                 :            :         ptls->copy_stack_ctx.uc_stack.ss_size = ssize;
    1388                 :            : #endif
    1389         [ #  # ]:          0 :         if (jl_setjmp(ptls->copy_stack_ctx.uc_mcontext, 0))
    1390                 :          0 :             start_task(); // sanitizer_finish_switch_fiber is part of start_task
    1391                 :          0 :         return ct;
    1392                 :            :     }
    1393                 :         15 :     ssize = JL_STACK_SIZE;
    1394                 :         15 :     char *stkbuf = jl_alloc_fiber(&ptls->base_ctx, &ssize, NULL);
    1395                 :         15 :     ptls->stackbase = stkbuf + ssize;
    1396                 :         15 :     ptls->stacksize = ssize;
    1397                 :            : #endif
    1398                 :            : 
    1399         [ +  - ]:         15 :     if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
    1400                 :         15 :         jl_install_thread_signal_handler(ptls);
    1401                 :            : 
    1402                 :         15 :     return ct;
    1403                 :            : }
    1404                 :            : 
    1405                 :          0 : JL_DLLEXPORT int jl_is_task_started(jl_task_t *t) JL_NOTSAFEPOINT
    1406                 :            : {
    1407                 :          0 :     return t->started;
    1408                 :            : }
    1409                 :            : 
    1410                 :      15117 : JL_DLLEXPORT int16_t jl_get_task_tid(jl_task_t *t) JL_NOTSAFEPOINT
    1411                 :            : {
    1412                 :      15117 :     return jl_atomic_load_relaxed(&t->tid);
    1413                 :            : }
    1414                 :            : 
    1415                 :          0 : JL_DLLEXPORT int8_t jl_get_task_threadpoolid(jl_task_t *t)
    1416                 :            : {
    1417                 :          0 :     return t->threadpoolid;
    1418                 :            : }
    1419                 :            : 
    1420                 :            : 
    1421                 :            : #ifdef _OS_WINDOWS_
    1422                 :            : #if defined(_CPU_X86_)
    1423                 :            : extern DWORD32 __readgsdword(int);
    1424                 :            : extern DWORD32 __readgs(void);
    1425                 :            : #endif
    1426                 :            : JL_DLLEXPORT void jl_gdb_dump_threadinfo(void)
    1427                 :            : {
    1428                 :            : #if defined(_CPU_X86_64_)
    1429                 :            :     DWORD64 gs0 = __readgsqword(0x0);
    1430                 :            :     DWORD64 gs8 = __readgsqword(0x8);
    1431                 :            :     DWORD64 gs16 = __readgsqword(0x10);
    1432                 :            :     jl_safe_printf("ThreadId: %u, Stack: %p -- %p to %p, SEH: %p\n",
    1433                 :            :                    (unsigned)GetCurrentThreadId(),
    1434                 :            :                    jl_get_frame_addr(),
    1435                 :            :                    (void*)gs8, (void*)gs16, (void*)gs0);
    1436                 :            : #elif defined(_CPU_X86_)
    1437                 :            :     DWORD32 fs0 = __readfsdword(0x0);
    1438                 :            :     DWORD32 fs4 = __readfsdword(0x4);
    1439                 :            :     DWORD32 fs8 = __readfsdword(0x8);
    1440                 :            :     jl_safe_printf("ThreadId: %u, Stack: %p -- %p to %p, SEH: %p\n",
    1441                 :            :                    (unsigned)GetCurrentThreadId(),
    1442                 :            :                    jl_get_frame_addr(),
    1443                 :            :                    (void*)fs4, (void*)fs8, (void*)fs0);
    1444                 :            :     if (__readgs()) { // WoW64 if GS is non-zero
    1445                 :            :         DWORD32 gs0 = __readgsdword(0x0);
    1446                 :            :         DWORD32 gs4 = __readgsdword(0x4);
    1447                 :            :         DWORD32 gs8 = __readgsdword(0x8);
    1448                 :            :         DWORD32 gs12 = __readgsdword(0xc);
    1449                 :            :         DWORD32 gs16 = __readgsdword(0x10);
    1450                 :            :         DWORD32 gs20 = __readgsdword(0x14);
    1451                 :            :         jl_safe_printf("Stack64: %p%p to %p%p, SEH64: %p%p\n",
    1452                 :            :                        (void*)gs12, (void*)gs8,
    1453                 :            :                        (void*)gs20, (void*)gs16,
    1454                 :            :                        (void*)gs4, (void*)gs0);
    1455                 :            :     }
    1456                 :            : #else
    1457                 :            :     jl_safe_printf("ThreadId: %u, Stack: %p\n",
    1458                 :            :                    (unsigned)GetCurrentThreadId(),
    1459                 :            :                    jl_get_frame_addr());
    1460                 :            : #endif
    1461                 :            : }
    1462                 :            : #endif
    1463                 :            : 
    1464                 :            : 
    1465                 :            : #ifdef __cplusplus
    1466                 :            : }
    1467                 :            : #endif

Generated by: LCOV version 1.14