|
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) |
|
28 { |
|
29 _pr_stackLock = PR_NewLock(); |
|
30 } |
|
31 |
|
32 void _PR_CleanupStacks(void) |
|
33 { |
|
34 if (_pr_stackLock) { |
|
35 PR_DestroyLock(_pr_stackLock); |
|
36 _pr_stackLock = NULL; |
|
37 } |
|
38 } |
|
39 |
|
40 /* |
|
41 ** Allocate a stack for a thread. |
|
42 */ |
|
43 PRThreadStack *_PR_NewStack(PRUint32 stackSize) |
|
44 { |
|
45 PRCList *qp; |
|
46 PRThreadStack *ts; |
|
47 PRThread *thr; |
|
48 |
|
49 /* |
|
50 ** Trim the list of free stacks. Trim it backwards, tossing out the |
|
51 ** oldest stack found first (this way more recent stacks have a |
|
52 ** chance of being present in the data cache). |
|
53 */ |
|
54 PR_Lock(_pr_stackLock); |
|
55 qp = _pr_freeStacks.prev; |
|
56 while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) { |
|
57 ts = _PR_THREAD_STACK_PTR(qp); |
|
58 thr = _PR_THREAD_STACK_TO_PTR(ts); |
|
59 qp = qp->prev; |
|
60 /* |
|
61 * skip stacks which are still being used |
|
62 */ |
|
63 if (thr->no_sched) |
|
64 continue; |
|
65 PR_REMOVE_LINK(&ts->links); |
|
66 |
|
67 /* Give platform OS to clear out the stack for debugging */ |
|
68 _PR_MD_CLEAR_STACK(ts); |
|
69 |
|
70 _pr_numFreeStacks--; |
|
71 _PR_DestroySegment(ts->seg); |
|
72 PR_DELETE(ts); |
|
73 } |
|
74 |
|
75 /* |
|
76 ** Find a free thread stack. This searches the list of free'd up |
|
77 ** virtually mapped thread stacks. |
|
78 */ |
|
79 qp = _pr_freeStacks.next; |
|
80 ts = 0; |
|
81 while (qp != &_pr_freeStacks) { |
|
82 ts = _PR_THREAD_STACK_PTR(qp); |
|
83 thr = _PR_THREAD_STACK_TO_PTR(ts); |
|
84 qp = qp->next; |
|
85 /* |
|
86 * skip stacks which are still being used |
|
87 */ |
|
88 if ((!(thr->no_sched)) && ((ts->allocSize - 2*REDZONE) >= stackSize)) { |
|
89 /* |
|
90 ** Found a stack that is not in use and is big enough. Change |
|
91 ** stackSize to fit it. |
|
92 */ |
|
93 stackSize = ts->allocSize - 2*REDZONE; |
|
94 PR_REMOVE_LINK(&ts->links); |
|
95 _pr_numFreeStacks--; |
|
96 ts->links.next = 0; |
|
97 ts->links.prev = 0; |
|
98 PR_Unlock(_pr_stackLock); |
|
99 goto done; |
|
100 } |
|
101 ts = 0; |
|
102 } |
|
103 PR_Unlock(_pr_stackLock); |
|
104 |
|
105 if (!ts) { |
|
106 /* Make a new thread stack object. */ |
|
107 ts = PR_NEWZAP(PRThreadStack); |
|
108 if (!ts) { |
|
109 return NULL; |
|
110 } |
|
111 |
|
112 /* |
|
113 ** Assign some of the virtual space to the new stack object. We |
|
114 ** may not get that piece of VM, but if nothing else we will |
|
115 ** advance the pointer so we don't collide (unless the OS screws |
|
116 ** up). |
|
117 */ |
|
118 ts->allocSize = stackSize + 2*REDZONE; |
|
119 ts->seg = _PR_NewSegment(ts->allocSize, 0); |
|
120 if (!ts->seg) { |
|
121 PR_DELETE(ts); |
|
122 return NULL; |
|
123 } |
|
124 } |
|
125 |
|
126 done: |
|
127 ts->allocBase = (char*)ts->seg->vaddr; |
|
128 ts->flags = _PR_STACK_MAPPED; |
|
129 ts->stackSize = stackSize; |
|
130 |
|
131 #ifdef HAVE_STACK_GROWING_UP |
|
132 ts->stackTop = ts->allocBase + REDZONE; |
|
133 ts->stackBottom = ts->stackTop + stackSize; |
|
134 #else |
|
135 ts->stackBottom = ts->allocBase + REDZONE; |
|
136 ts->stackTop = ts->stackBottom + stackSize; |
|
137 #endif |
|
138 |
|
139 PR_LOG(_pr_thread_lm, PR_LOG_NOTICE, |
|
140 ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n", |
|
141 ts->allocBase, ts->allocBase + ts->allocSize - 1, |
|
142 ts->allocBase + REDZONE, |
|
143 ts->allocBase + REDZONE + stackSize - 1)); |
|
144 |
|
145 _PR_MD_INIT_STACK(ts,REDZONE); |
|
146 |
|
147 return ts; |
|
148 } |
|
149 |
|
150 /* |
|
151 ** Free the stack for the current thread |
|
152 */ |
|
153 void _PR_FreeStack(PRThreadStack *ts) |
|
154 { |
|
155 if (!ts) { |
|
156 return; |
|
157 } |
|
158 if (ts->flags & _PR_STACK_PRIMORDIAL) { |
|
159 PR_DELETE(ts); |
|
160 return; |
|
161 } |
|
162 |
|
163 /* |
|
164 ** Put the stack on the free list. This is done because we are still |
|
165 ** using the stack. Next time a thread is created we will trim the |
|
166 ** list down; it's safe to do it then because we will have had to |
|
167 ** context switch to a live stack before another thread can be |
|
168 ** created. |
|
169 */ |
|
170 PR_Lock(_pr_stackLock); |
|
171 PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev); |
|
172 _pr_numFreeStacks++; |
|
173 PR_Unlock(_pr_stackLock); |
|
174 } |