tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prustack.c (4448B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "primpl.h"
      7 
      8 /* List of free stack virtual memory chunks */
      9 PRLock* _pr_stackLock;
     10 PRCList _pr_freeStacks = PR_INIT_STATIC_CLIST(&_pr_freeStacks);
     11 PRIntn _pr_numFreeStacks;
     12 PRIntn _pr_maxFreeStacks = 4;
     13 
     14 #ifdef DEBUG
     15 /*
     16 ** A variable that can be set via the debugger...
     17 */
     18 PRBool _pr_debugStacks = PR_FALSE;
     19 #endif
     20 
     21 /* How much space to leave between the stacks, at each end */
     22 #define REDZONE (2 << _pr_pageShift)
     23 
     24 #define _PR_THREAD_STACK_PTR(_qp) \
     25  ((PRThreadStack*)((char*)(_qp) - offsetof(PRThreadStack, links)))
     26 
     27 void _PR_InitStacks(void) { _pr_stackLock = PR_NewLock(); }
     28 
     29 void _PR_CleanupStacks(void) {
     30  if (_pr_stackLock) {
     31    PR_DestroyLock(_pr_stackLock);
     32    _pr_stackLock = NULL;
     33  }
     34 }
     35 
     36 /*
     37 ** Allocate a stack for a thread.
     38 */
     39 PRThreadStack* _PR_NewStack(PRUint32 stackSize) {
     40  PRCList* qp;
     41  PRThreadStack* ts;
     42  PRThread* thr;
     43 
     44  /*
     45  ** Trim the list of free stacks. Trim it backwards, tossing out the
     46  ** oldest stack found first (this way more recent stacks have a
     47  ** chance of being present in the data cache).
     48  */
     49  PR_Lock(_pr_stackLock);
     50  qp = _pr_freeStacks.prev;
     51  while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) {
     52    ts = _PR_THREAD_STACK_PTR(qp);
     53    thr = _PR_THREAD_STACK_TO_PTR(ts);
     54    qp = qp->prev;
     55    /*
     56     * skip stacks which are still being used
     57     */
     58    if (thr->no_sched) {
     59      continue;
     60    }
     61    PR_REMOVE_LINK(&ts->links);
     62 
     63    /* Give platform OS to clear out the stack for debugging */
     64    _PR_MD_CLEAR_STACK(ts);
     65 
     66    _pr_numFreeStacks--;
     67    _PR_DestroySegment(ts->seg);
     68    PR_DELETE(ts);
     69  }
     70 
     71  /*
     72  ** Find a free thread stack. This searches the list of free'd up
     73  ** virtually mapped thread stacks.
     74  */
     75  qp = _pr_freeStacks.next;
     76  ts = 0;
     77  while (qp != &_pr_freeStacks) {
     78    ts = _PR_THREAD_STACK_PTR(qp);
     79    thr = _PR_THREAD_STACK_TO_PTR(ts);
     80    qp = qp->next;
     81    /*
     82     * skip stacks which are still being used
     83     */
     84    if ((!(thr->no_sched)) && ((ts->allocSize - 2 * REDZONE) >= stackSize)) {
     85      /*
     86      ** Found a stack that is not in use and is big enough. Change
     87      ** stackSize to fit it.
     88      */
     89      stackSize = ts->allocSize - 2 * REDZONE;
     90      PR_REMOVE_LINK(&ts->links);
     91      _pr_numFreeStacks--;
     92      ts->links.next = 0;
     93      ts->links.prev = 0;
     94      PR_Unlock(_pr_stackLock);
     95      goto done;
     96    }
     97    ts = 0;
     98  }
     99  PR_Unlock(_pr_stackLock);
    100 
    101  if (!ts) {
    102    /* Make a new thread stack object. */
    103    ts = PR_NEWZAP(PRThreadStack);
    104    if (!ts) {
    105      return NULL;
    106    }
    107 
    108    /*
    109    ** Assign some of the virtual space to the new stack object. We
    110    ** may not get that piece of VM, but if nothing else we will
    111    ** advance the pointer so we don't collide (unless the OS screws
    112    ** up).
    113    */
    114    ts->allocSize = stackSize + 2 * REDZONE;
    115    ts->seg = _PR_NewSegment(ts->allocSize, 0);
    116    if (!ts->seg) {
    117      PR_DELETE(ts);
    118      return NULL;
    119    }
    120  }
    121 
    122 done:
    123  ts->allocBase = (char*)ts->seg->vaddr;
    124  ts->flags = _PR_STACK_MAPPED;
    125  ts->stackSize = stackSize;
    126 
    127 #ifdef HAVE_STACK_GROWING_UP
    128  ts->stackTop = ts->allocBase + REDZONE;
    129  ts->stackBottom = ts->stackTop + stackSize;
    130 #else
    131  ts->stackBottom = ts->allocBase + REDZONE;
    132  ts->stackTop = ts->stackBottom + stackSize;
    133 #endif
    134 
    135  PR_LOG(_pr_thread_lm, PR_LOG_NOTICE,
    136         ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n",
    137          ts->allocBase, ts->allocBase + ts->allocSize - 1,
    138          ts->allocBase + REDZONE, ts->allocBase + REDZONE + stackSize - 1));
    139 
    140  _PR_MD_INIT_STACK(ts, REDZONE);
    141 
    142  return ts;
    143 }
    144 
    145 /*
    146 ** Free the stack for the current thread
    147 */
    148 void _PR_FreeStack(PRThreadStack* ts) {
    149  if (!ts) {
    150    return;
    151  }
    152  if (ts->flags & _PR_STACK_PRIMORDIAL) {
    153    PR_DELETE(ts);
    154    return;
    155  }
    156 
    157  /*
    158  ** Put the stack on the free list. This is done because we are still
    159  ** using the stack. Next time a thread is created we will trim the
    160  ** list down; it's safe to do it then because we will have had to
    161  ** context switch to a live stack before another thread can be
    162  ** created.
    163  */
    164  PR_Lock(_pr_stackLock);
    165  PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev);
    166  _pr_numFreeStacks++;
    167  PR_Unlock(_pr_stackLock);
    168 }