LCOV - code coverage report
Current view: top level - src - gc-stacks.c (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 82 122 67.2 %
Date: 2022-07-17 01:01:28 Functions: 6 9 66.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 35 60 58.3 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : #include "gc.h"
       4                 :            : #ifndef _OS_WINDOWS_
       5                 :            : #  include <sys/resource.h>
       6                 :            : #endif
       7                 :            : 
       8                 :            : #ifdef _P64
       9                 :            : # ifdef _OS_WINDOWS_
      10                 :            : #  define MAX_STACK_MAPPINGS 500
      11                 :            : # else
      12                 :            : #  define MAX_STACK_MAPPINGS 30000
      13                 :            : # endif
      14                 :            : #else
      15                 :            : # ifdef _OS_WINDOWS_
      16                 :            : #  define MAX_STACK_MAPPINGS 250
      17                 :            : # else
      18                 :            : #  define MAX_STACK_MAPPINGS 500
      19                 :            : # endif
      20                 :            : #endif
      21                 :            : 
      22                 :            : // number of stacks to always keep available per pool
      23                 :            : #define MIN_STACK_MAPPINGS_PER_POOL 5
      24                 :            : 
      25                 :            : const size_t jl_guard_size = (4096 * 8);
      26                 :            : static _Atomic(uint32_t) num_stack_mappings = 0;
      27                 :            : 
      28                 :            : #ifdef _OS_WINDOWS_
      29                 :            : #define MAP_FAILED NULL
      30                 :            : static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT
      31                 :            : {
      32                 :            :     void *stk = VirtualAlloc(NULL, bufsz, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
      33                 :            :     if (stk == NULL)
      34                 :            :         return MAP_FAILED;
      35                 :            :     DWORD dwOldProtect;
      36                 :            :     if (!VirtualProtect(stk, jl_guard_size, PAGE_READWRITE | PAGE_GUARD, &dwOldProtect)) {
      37                 :            :         VirtualFree(stk, 0, MEM_RELEASE);
      38                 :            :         return MAP_FAILED;
      39                 :            :     }
      40                 :            :     jl_atomic_fetch_add(&num_stack_mappings, 1);
      41                 :            :     return stk;
      42                 :            : }
      43                 :            : 
      44                 :            : 
      45                 :            : static void free_stack(void *stkbuf, size_t bufsz)
      46                 :            : {
      47                 :            :     VirtualFree(stkbuf, 0, MEM_RELEASE);
      48                 :            :     jl_atomic_fetch_add(&num_stack_mappings, -1);
      49                 :            : }
      50                 :            : 
      51                 :            : #else
      52                 :            : 
      53                 :         71 : static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT
      54                 :            : {
      55                 :         71 :     void* stk = mmap(0, bufsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
      56         [ -  + ]:         71 :     if (stk == MAP_FAILED)
      57                 :          0 :         return MAP_FAILED;
      58                 :            : #if !defined(JL_HAVE_UCONTEXT) && !defined(JL_HAVE_SIGALTSTACK)
      59                 :            :     // setup a guard page to detect stack overflow
      60         [ -  + ]:         71 :     if (mprotect(stk, jl_guard_size, PROT_NONE) == -1) {
      61                 :          0 :         munmap(stk, bufsz);
      62                 :          0 :         return MAP_FAILED;
      63                 :            :     }
      64                 :            : #endif
      65                 :         71 :     jl_atomic_fetch_add(&num_stack_mappings, 1);
      66                 :         71 :     return stk;
      67                 :            : }
      68                 :            : 
      69                 :          9 : static void free_stack(void *stkbuf, size_t bufsz)
      70                 :            : {
      71                 :          9 :     munmap(stkbuf, bufsz);
      72                 :          9 :     jl_atomic_fetch_add(&num_stack_mappings, -1);
      73                 :          9 : }
      74                 :            : #endif
      75                 :            : 
      76                 :            : 
      77                 :            : const unsigned pool_sizes[] = {
      78                 :            :     128 * 1024,
      79                 :            :     192 * 1024,
      80                 :            :     256 * 1024,
      81                 :            :     384 * 1024,
      82                 :            :     512 * 1024,
      83                 :            :     768 * 1024,
      84                 :            :     1024 * 1024,
      85                 :            :     1537 * 1024,
      86                 :            :     2048 * 1024,
      87                 :            :     3 * 1024 * 1024,
      88                 :            :     4 * 1024 * 1024,
      89                 :            :     6 * 1024 * 1024,
      90                 :            :     8 * 1024 * 1024,
      91                 :            :     12 * 1024 * 1024,
      92                 :            :     16 * 1024 * 1024,
      93                 :            :     24 * 1024 * 1024,
      94                 :            : };
      95                 :            : 
      96                 :            : static_assert(sizeof(pool_sizes) == JL_N_STACK_POOLS * sizeof(pool_sizes[0]), "JL_N_STACK_POOLS size mismatch");
      97                 :            : 
      98                 :        308 : static unsigned select_pool(size_t nb) JL_NOTSAFEPOINT
      99                 :            : {
     100                 :        308 :     unsigned pool_id = 0;
     101         [ +  + ]:       3418 :     while (pool_sizes[pool_id] < nb)
     102                 :       3110 :         pool_id++;
     103                 :        308 :     return pool_id;
     104                 :            : }
     105                 :            : 
     106                 :            : 
     107                 :          0 : static void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz)
     108                 :            : {
     109         [ #  # ]:          0 :     if (bufsz <= pool_sizes[JL_N_STACK_POOLS - 1]) {
     110                 :          0 :         unsigned pool_id = select_pool(bufsz);
     111         [ #  # ]:          0 :         if (pool_sizes[pool_id] == bufsz) {
     112                 :          0 :             arraylist_push(&ptls->heap.free_stacks[pool_id], stkbuf);
     113                 :          0 :             return;
     114                 :            :         }
     115                 :            :     }
     116                 :          0 :     free_stack(stkbuf, bufsz);
     117                 :            : }
     118                 :            : 
     119                 :            : 
     120                 :          0 : JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz)
     121                 :            : {
     122                 :          0 :     jl_task_t *ct = jl_current_task;
     123                 :          0 :     _jl_free_stack(ct->ptls, stkbuf, bufsz);
     124                 :          0 : }
     125                 :            : 
     126                 :            : 
     127                 :        130 : void jl_release_task_stack(jl_ptls_t ptls, jl_task_t *task)
     128                 :            : {
     129                 :            :     // avoid adding an original thread stack to the free list
     130   [ -  +  -  - ]:        130 :     if (task == ptls->root_task && !task->copy_stack)
     131                 :          0 :         return;
     132                 :        130 :     void *stkbuf = task->stkbuf;
     133                 :        130 :     size_t bufsz = task->bufsz;
     134         [ +  - ]:        130 :     if (bufsz <= pool_sizes[JL_N_STACK_POOLS - 1]) {
     135                 :        130 :         unsigned pool_id = select_pool(bufsz);
     136         [ +  - ]:        130 :         if (pool_sizes[pool_id] == bufsz) {
     137                 :        130 :             task->stkbuf = NULL;
     138                 :        130 :             arraylist_push(&ptls->heap.free_stacks[pool_id], stkbuf);
     139                 :            :         }
     140                 :            :     }
     141                 :            : }
     142                 :            : 
     143                 :            : 
     144                 :        178 : JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, jl_task_t *owner) JL_NOTSAFEPOINT
     145                 :            : {
     146                 :        178 :     jl_task_t *ct = jl_current_task;
     147                 :        178 :     jl_ptls_t ptls = ct->ptls;
     148                 :        178 :     size_t ssize = *bufsz;
     149                 :        178 :     void *stk = NULL;
     150         [ +  - ]:        178 :     if (ssize <= pool_sizes[JL_N_STACK_POOLS - 1]) {
     151                 :        178 :         unsigned pool_id = select_pool(ssize);
     152                 :        178 :         ssize = pool_sizes[pool_id];
     153                 :        178 :         arraylist_t *pool = &ptls->heap.free_stacks[pool_id];
     154         [ +  + ]:        178 :         if (pool->len > 0) {
     155                 :        107 :             stk = arraylist_pop(pool);
     156                 :            :         }
     157                 :            :     }
     158                 :            :     else {
     159                 :          0 :         ssize = LLT_ALIGN(ssize, jl_page_size);
     160                 :            :     }
     161         [ +  + ]:        178 :     if (stk == NULL) {
     162         [ -  + ]:         71 :         if (jl_atomic_load_relaxed(&num_stack_mappings) >= MAX_STACK_MAPPINGS)
     163                 :            :             // we accept that this can go over by as much as nthreads since it's not a CAS
     164                 :          0 :             return NULL;
     165                 :            :         // TODO: allocate blocks of stacks? but need to mprotect individually anyways
     166                 :         71 :         stk = malloc_stack(ssize);
     167         [ -  + ]:         71 :         if (stk == MAP_FAILED)
     168                 :          0 :             return NULL;
     169                 :            :     }
     170                 :        178 :     *bufsz = ssize;
     171         [ +  + ]:        178 :     if (owner) {
     172                 :        148 :         arraylist_t *live_tasks = &ptls->heap.live_tasks;
     173                 :        148 :         arraylist_push(live_tasks, owner);
     174                 :            :     }
     175                 :        178 :     return stk;
     176                 :            : }
     177                 :            : 
     178                 :       1179 : void sweep_stack_pools(void)
     179                 :            : {
     180                 :            :     // Stack sweeping algorithm:
     181                 :            :     //    // deallocate stacks if we have too many sitting around unused
     182                 :            :     //    for (stk in halfof(free_stacks))
     183                 :            :     //        free_stack(stk, pool_sz);
     184                 :            :     //    // then sweep the task stacks
     185                 :            :     //    for (t in live_tasks)
     186                 :            :     //        if (!gc-marked(t))
     187                 :            :     //            stkbuf = t->stkbuf
     188                 :            :     //            bufsz = t->bufsz
     189                 :            :     //            if (stkbuf)
     190                 :            :     //                push(free_stacks[sz], stkbuf)
     191         [ +  + ]:       2358 :     for (int i = 0; i < jl_n_threads; i++) {
     192                 :       1179 :         jl_ptls_t ptls2 = jl_all_tls_states[i];
     193                 :            : 
     194                 :            :         // free half of stacks that remain unused since last sweep
     195         [ +  + ]:      20043 :         for (int p = 0; p < JL_N_STACK_POOLS; p++) {
     196                 :      18864 :             arraylist_t *al = &ptls2->heap.free_stacks[p];
     197                 :            :             size_t n_to_free;
     198         [ +  + ]:      18864 :             if (al->len > MIN_STACK_MAPPINGS_PER_POOL) {
     199                 :          4 :                 n_to_free = al->len / 2;
     200         [ +  - ]:          4 :                 if (n_to_free > (al->len - MIN_STACK_MAPPINGS_PER_POOL))
     201                 :          4 :                     n_to_free = al->len - MIN_STACK_MAPPINGS_PER_POOL;
     202                 :            :             }
     203                 :            :             else {
     204                 :      18860 :                 n_to_free = 0;
     205                 :            :             }
     206         [ +  + ]:      18873 :             for (int n = 0; n < n_to_free; n++) {
     207                 :          9 :                 void *stk = arraylist_pop(al);
     208                 :          9 :                 free_stack(stk, pool_sizes[p]);
     209                 :            :             }
     210                 :            :         }
     211                 :            : 
     212                 :       1179 :         arraylist_t *live_tasks = &ptls2->heap.live_tasks;
     213                 :       1179 :         size_t n = 0;
     214                 :       1179 :         size_t ndel = 0;
     215                 :       1179 :         size_t l = live_tasks->len;
     216                 :       1179 :         void **lst = live_tasks->items;
     217         [ +  + ]:       1179 :         if (l == 0)
     218                 :        798 :             continue;
     219                 :        845 :         while (1) {
     220                 :       1226 :             jl_task_t *t = (jl_task_t*)lst[n];
     221         [ -  + ]:       1226 :             assert(jl_is_task(t));
     222         [ +  + ]:       1226 :             if (gc_marked(jl_astaggedvalue(t)->bits.gc)) {
     223         [ +  + ]:       1106 :                 if (t->stkbuf == NULL)
     224                 :          5 :                     ndel++; // jl_release_task_stack called
     225                 :            :                 else
     226                 :       1101 :                     n++;
     227                 :            :             }
     228                 :            :             else {
     229                 :        120 :                 ndel++;
     230                 :        120 :                 void *stkbuf = t->stkbuf;
     231                 :        120 :                 size_t bufsz = t->bufsz;
     232         [ -  + ]:        120 :                 if (stkbuf) {
     233                 :          0 :                     t->stkbuf = NULL;
     234                 :          0 :                     _jl_free_stack(ptls2, stkbuf, bufsz);
     235                 :            :                 }
     236                 :            : #ifdef _COMPILER_TSAN_ENABLED_
     237                 :            :                 if (t->ctx.tsan_state) {
     238                 :            :                     __tsan_destroy_fiber(t->ctx.tsan_state);
     239                 :            :                     t->ctx.tsan_state = NULL;
     240                 :            :                 }
     241                 :            : #endif
     242                 :            :             }
     243         [ +  + ]:       1226 :             if (n >= l - ndel)
     244                 :        381 :                 break;
     245                 :        845 :             void *tmp = lst[n];
     246                 :        845 :             lst[n] = lst[n + ndel];
     247                 :        845 :             lst[n + ndel] = tmp;
     248                 :            :         }
     249                 :        381 :         live_tasks->len -= ndel;
     250                 :            :     }
     251                 :       1179 : }
     252                 :            : 
     253                 :          0 : JL_DLLEXPORT jl_array_t *jl_live_tasks(void)
     254                 :            : {
     255                 :          0 :     jl_task_t *ct = jl_current_task;
     256                 :          0 :     jl_ptls_t ptls = ct->ptls;
     257                 :          0 :     arraylist_t *live_tasks = &ptls->heap.live_tasks;
     258                 :            :     size_t i, j, l;
     259                 :            :     jl_array_t *a;
     260                 :            :     do {
     261                 :          0 :         l = live_tasks->len;
     262                 :          0 :         a = jl_alloc_vec_any(l + 1); // may gc, changing the number of tasks
     263         [ #  # ]:          0 :     } while (l + 1 < live_tasks->len);
     264                 :          0 :     l = live_tasks->len;
     265                 :          0 :     void **lst = live_tasks->items;
     266                 :          0 :     j = 0;
     267                 :          0 :     ((void**)jl_array_data(a))[j++] = ptls->root_task;
     268         [ #  # ]:          0 :     for (i = 0; i < l; i++) {
     269         [ #  # ]:          0 :         if (((jl_task_t*)lst[i])->stkbuf != NULL)
     270                 :          0 :             ((void**)jl_array_data(a))[j++] = lst[i];
     271                 :            :     }
     272                 :          0 :     l = jl_array_len(a);
     273         [ #  # ]:          0 :     if (j < l) {
     274                 :          0 :         JL_GC_PUSH1(&a);
     275                 :          0 :         jl_array_del_end(a, l - j);
     276                 :          0 :         JL_GC_POP();
     277                 :            :     }
     278                 :          0 :     return a;
     279                 :            : }

Generated by: LCOV version 1.14