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