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