andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "primpl.h" andre@0: andre@0: /* List of free stack virtual memory chunks */ andre@0: PRLock *_pr_stackLock; andre@0: PRCList _pr_freeStacks = PR_INIT_STATIC_CLIST(&_pr_freeStacks); andre@0: PRIntn _pr_numFreeStacks; andre@0: PRIntn _pr_maxFreeStacks = 4; andre@0: andre@0: #ifdef DEBUG andre@0: /* andre@0: ** A variable that can be set via the debugger... andre@0: */ andre@0: PRBool _pr_debugStacks = PR_FALSE; andre@0: #endif andre@0: andre@0: /* How much space to leave between the stacks, at each end */ andre@0: #define REDZONE (2 << _pr_pageShift) andre@0: andre@0: #define _PR_THREAD_STACK_PTR(_qp) \ andre@0: ((PRThreadStack*) ((char*) (_qp) - offsetof(PRThreadStack,links))) andre@0: andre@0: void _PR_InitStacks(void) andre@0: { andre@0: _pr_stackLock = PR_NewLock(); andre@0: } andre@0: andre@0: void _PR_CleanupStacks(void) andre@0: { andre@0: if (_pr_stackLock) { andre@0: PR_DestroyLock(_pr_stackLock); andre@0: _pr_stackLock = NULL; andre@0: } andre@0: } andre@0: andre@0: /* andre@0: ** Allocate a stack for a thread. andre@0: */ andre@0: PRThreadStack *_PR_NewStack(PRUint32 stackSize) andre@0: { andre@0: PRCList *qp; andre@0: PRThreadStack *ts; andre@0: PRThread *thr; andre@0: andre@0: /* andre@0: ** Trim the list of free stacks. Trim it backwards, tossing out the andre@0: ** oldest stack found first (this way more recent stacks have a andre@0: ** chance of being present in the data cache). andre@0: */ andre@0: PR_Lock(_pr_stackLock); andre@0: qp = _pr_freeStacks.prev; andre@0: while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) { andre@0: ts = _PR_THREAD_STACK_PTR(qp); andre@0: thr = _PR_THREAD_STACK_TO_PTR(ts); andre@0: qp = qp->prev; andre@0: /* andre@0: * skip stacks which are still being used andre@0: */ andre@0: if (thr->no_sched) andre@0: continue; andre@0: PR_REMOVE_LINK(&ts->links); andre@0: andre@0: /* Give platform OS to clear out the stack for debugging */ andre@0: _PR_MD_CLEAR_STACK(ts); andre@0: andre@0: _pr_numFreeStacks--; andre@0: _PR_DestroySegment(ts->seg); andre@0: PR_DELETE(ts); andre@0: } andre@0: andre@0: /* andre@0: ** Find a free thread stack. This searches the list of free'd up andre@0: ** virtually mapped thread stacks. andre@0: */ andre@0: qp = _pr_freeStacks.next; andre@0: ts = 0; andre@0: while (qp != &_pr_freeStacks) { andre@0: ts = _PR_THREAD_STACK_PTR(qp); andre@0: thr = _PR_THREAD_STACK_TO_PTR(ts); andre@0: qp = qp->next; andre@0: /* andre@0: * skip stacks which are still being used andre@0: */ andre@0: if ((!(thr->no_sched)) && ((ts->allocSize - 2*REDZONE) >= stackSize)) { andre@0: /* andre@0: ** Found a stack that is not in use and is big enough. Change andre@0: ** stackSize to fit it. andre@0: */ andre@0: stackSize = ts->allocSize - 2*REDZONE; andre@0: PR_REMOVE_LINK(&ts->links); andre@0: _pr_numFreeStacks--; andre@0: ts->links.next = 0; andre@0: ts->links.prev = 0; andre@0: PR_Unlock(_pr_stackLock); andre@0: goto done; andre@0: } andre@0: ts = 0; andre@0: } andre@0: PR_Unlock(_pr_stackLock); andre@0: andre@0: if (!ts) { andre@0: /* Make a new thread stack object. */ andre@0: ts = PR_NEWZAP(PRThreadStack); andre@0: if (!ts) { andre@0: return NULL; andre@0: } andre@0: andre@0: /* andre@0: ** Assign some of the virtual space to the new stack object. We andre@0: ** may not get that piece of VM, but if nothing else we will andre@0: ** advance the pointer so we don't collide (unless the OS screws andre@0: ** up). andre@0: */ andre@0: ts->allocSize = stackSize + 2*REDZONE; andre@0: ts->seg = _PR_NewSegment(ts->allocSize, 0); andre@0: if (!ts->seg) { andre@0: PR_DELETE(ts); andre@0: return NULL; andre@0: } andre@0: } andre@0: andre@0: done: andre@0: ts->allocBase = (char*)ts->seg->vaddr; andre@0: ts->flags = _PR_STACK_MAPPED; andre@0: ts->stackSize = stackSize; andre@0: andre@0: #ifdef HAVE_STACK_GROWING_UP andre@0: ts->stackTop = ts->allocBase + REDZONE; andre@0: ts->stackBottom = ts->stackTop + stackSize; andre@0: #else andre@0: ts->stackBottom = ts->allocBase + REDZONE; andre@0: ts->stackTop = ts->stackBottom + stackSize; andre@0: #endif andre@0: andre@0: PR_LOG(_pr_thread_lm, PR_LOG_NOTICE, andre@0: ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n", andre@0: ts->allocBase, ts->allocBase + ts->allocSize - 1, andre@0: ts->allocBase + REDZONE, andre@0: ts->allocBase + REDZONE + stackSize - 1)); andre@0: andre@0: _PR_MD_INIT_STACK(ts,REDZONE); andre@0: andre@0: return ts; andre@0: } andre@0: andre@0: /* andre@0: ** Free the stack for the current thread andre@0: */ andre@0: void _PR_FreeStack(PRThreadStack *ts) andre@0: { andre@0: if (!ts) { andre@0: return; andre@0: } andre@0: if (ts->flags & _PR_STACK_PRIMORDIAL) { andre@0: PR_DELETE(ts); andre@0: return; andre@0: } andre@0: andre@0: /* andre@0: ** Put the stack on the free list. This is done because we are still andre@0: ** using the stack. Next time a thread is created we will trim the andre@0: ** list down; it's safe to do it then because we will have had to andre@0: ** context switch to a live stack before another thread can be andre@0: ** created. andre@0: */ andre@0: PR_Lock(_pr_stackLock); andre@0: PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev); andre@0: _pr_numFreeStacks++; andre@0: PR_Unlock(_pr_stackLock); andre@0: }