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 }