1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is the Netscape Portable Runtime (NSPR).
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998-2000
20
* the Initial Developer. All Rights Reserved.
24
* Alternatively, the contents of this file may be used under the terms of
25
* either the GNU General Public License Version 2 or later (the "GPL"), or
26
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
* in which case the provisions of the GPL or the LGPL are applicable instead
28
* of those above. If you wish to allow use of your version of this file only
29
* under the terms of either the GPL or the LGPL, and not to allow others to
30
* use your version of this file under the terms of the MPL, indicate your
31
* decision by deleting the provisions above and replace them with the notice
32
* and other provisions required by the GPL or the LGPL. If you do not delete
33
* the provisions above, a recipient may use your version of this file under
34
* the terms of any one of the MPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
39
#include <sys/timeb.h>
43
** DispatchTrace -- define a thread dispatch trace entry
45
** The DispatchTrace oject(s) are instantiated in a single
46
** array. Think of the array as a push-down stack; entry
47
** zero is the most recent, entry one the next most recent, etc.
48
** For each time PR_MD_RESTORE_CONTEXT() is called, the array
49
** is Pushed down and entry zero is overwritten with data
50
** for the newly dispatched thread.
52
** Function TraceDispatch() manages the DispatchTrace array.
55
typedef struct DispatchTrace
59
PRInt16 mdThreadNumber;
61
PRThreadPriority priority;
63
} DispatchTrace, *DispatchTracePtr ;
65
static void TraceDispatch( PRThread *thread );
68
PRThread *_pr_primordialThread;
71
** Note: the static variables must be on the data-segment because
72
** the stack is destroyed during shadow-stack copy operations.
75
static char * pSource; /* ptr to sourc of a "shadow-stack" copy */
76
static char * pTarget; /* ptr to target of a "shadow-stack" copy */
77
static int cxByteCount; /* number of bytes for "shadow-stack" copy */
78
static int bytesMoved; /* instrumentation: WRT "shadow-stack" copy */
79
static FILE * file1 = 0; /* instrumentation: WRT debug */
81
#define NUM_DISPATCHTRACE_OBJECTS 24
82
static DispatchTrace dt[NUM_DISPATCHTRACE_OBJECTS] = {0}; /* instrumentation: WRT dispatch */
83
static PRUint32 dispatchCount = 0; /* instrumentation: number of thread dispatches */
85
static int OldPriorityOfPrimaryThread = -1;
86
static int TimeSlicesOnNonPrimaryThread = 0;
87
static PRUint32 threadNumber = 1; /* Instrumentation: monotonically increasing number */
92
** _PR_MD_FINAL_INIT() -- Final MD Initialization
94
** Poultry Problems! ... The stack, as allocated by PR_NewStack()
95
** is called from here, late in initialization, because PR_NewStack()
96
** requires lots of things working. When some elements of the
97
** primordial thread are created, early in initialization, the
98
** shadow stack is not one of these things. The "shadow stack" is
99
** created here, late in initiailization using PR_NewStack(), to
100
** ensure consistency in creation of the related objects.
102
** A new ThreadStack, and all its affiliated structures, is allocated
103
** via the call to PR_NewStack(). The PRThread structure in the
104
** new stack is ignored; the old PRThread structure is used (why?).
105
** The old PRThreadStack structure is abandoned.
111
PRThreadStack * stack = 0;
112
PRInt32 stacksize = 0;
113
PRThread * me = _PR_MD_CURRENT_THREAD();
115
_PR_ADJUST_STACKSIZE( stacksize );
116
stack = _PR_NewStack( stacksize );
122
} /* --- end _PR_MD_FINAL_INIT() --- */
126
_MD_INIT_RUNNING_CPU( struct _PRCPU *cpu )
128
PR_INIT_CLIST(&(cpu->md.ioQ));
129
cpu->md.ioq_max_osfd = -1;
130
cpu->md.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
141
** _PR_MD_INIT_STACK() -- Win16 specific Stack initialization.
147
_PR_MD_INIT_STACK( PRThreadStack *ts, PRIntn redzone )
149
ts->md.stackTop = ts->stackTop - sizeof(PRThread);
150
ts->md.cxByteCount = 0;
153
} /* --- end _PR_MD_INIT_STACK() --- */
156
** _PR_MD_INIT_THREAD() -- Win16 specific Thread initialization.
160
_PR_MD_INIT_THREAD(PRThread *thread)
162
if ( thread->flags & _PR_PRIMORDIAL)
164
_pr_primordialThread = thread;
165
thread->md.threadNumber = 1;
169
thread->md.threadNumber = ++threadNumber;
172
thread->md.magic = _MD_MAGIC_THREAD;
173
strcpy( thread->md.guardBand, "GuardBand" );
180
_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
182
_MD_SWITCH_CONTEXT( thread );
184
return( PR_SUCCESS );
187
void *PR_W16GetExceptionContext(void)
189
return _MD_CURRENT_THREAD()->md.exceptionContext;
193
PR_W16SetExceptionContext(void *context)
195
_MD_CURRENT_THREAD()->md.exceptionContext = context;
202
** _MD_RESTORE_CONTEXT() -- Resume execution of thread 't'.
204
** Win16 threading is based on the NSPR 2.0 general model of
205
** user threads. It differs from the general model in that a
206
** single "real" stack segment is used for execution of all
207
** threads. The context of the suspended threads is preserved
208
** in the md.context [and related members] of the PRThread
209
** structure. The stack context of the suspended thread is
210
** preserved in a "shadow stack" object.
212
** _MD_RESTORE_CONTEXT() implements most of the thread switching
213
** for NSPR's implementation of Win16 theads.
217
** Function PR_NewStack() in prustack.c allocates a new
218
** PRThreadStack, PRStack, PRSegment, and a "shadow" stack
219
** for a thread. These structures are wired together to
220
** form the basis of Win16 threads. The thread and shadow
221
** stack structures are created as part of PR_CreateThread().
223
** Note! Some special "magic" is applied to the "primordial"
224
** thread. The physical layout of the PRThread, PRThreadStack,
225
** shadow stack, ... is somewhat different. Watch yourself when
226
** mucking around with it. ... See _PR_MD_FINAL_INIT() for most
227
** of the special treatment of the primordial thread.
229
** Function _PR_MD_INIT_STACK() initializes the value of
230
** PRThreadStack member md.cxByteCount to zero; there
231
** is no context to be restored for a thread's initial
232
** dispatch. The value of member md.stackTop is set to
233
** point to the highest usable address on the shadow stack.
234
** This point corresponds to _pr_top_of_task_stack on the
235
** system's operating stack.
237
** _pr_top_of_task_stack points to a place on the system stack
238
** considered to be "close to the top". Stack context is preserved
239
** relative to this point.
241
** Reminder: In x86 architecture, the stack grows "down".
242
** That is: the stack pointer (SP register) is decremented
243
** to push objects onto the stack or when a call is made.
245
** Function _PR_MD_WAIT() invokes macro _MD_SWITCH_CONTEXT();
246
** this causes the hardware registers to be preserved in a
247
** CATCHBUF structure using function Catch() [see _win16.h],
248
** then calls PR_Schedule() to select a new thread for dispatch.
249
** PR_Schedule() calls _MD_RESTORE_CONTEXT() to cause the thread
250
** being suspended's stack to be preserved, to restore the
251
** stack of the to-be-dispactched thread, and to restore the
252
** to-be-dispactched thread's hardware registers.
254
** At the moment _PR_MD_RESTORE_CONTEXT() is called, the stack
255
** pointer (SP) is less than the reference pointer
256
** _pr_top_of_task_stack. The distance difference between the SP and
257
** _pr_top_of_task_stack is the amount of stack that must be preserved.
258
** This value, cxByteCount, is calculated then preserved in the
259
** PRThreadStack.md.cxByteCount for later use (size of stack
260
** context to restore) when this thread is dispatched again.
262
** A C language for() loop is used to copy, byte-by-byte, the
263
** stack data being preserved starting at the "address of t"
264
** [Note: 't' is the argument passed to _PR_MD_RESTORE_CONTEXT()]
265
** for the length of cxByteCount.
267
** variables pSource and pTarget are the calculated source and
268
** destination pointers for the stack copy operation. These
269
** variables are static scope because they cannot be instantiated
270
** on the stack itself, since the stack is clobbered by restoring
271
** the to-be-dispatched thread's stack context.
273
** After preserving the suspended thread's stack and architectural
274
** context, the to-be-dispatched thread's stack context is copied
275
** from its shadow stack to the system operational stack. The copy
276
** is done in a small fragment of in-line assembly language. Note:
277
** In NSPR 1.0, a while() loop was used to do the copy; when compiled
278
** with the MS C 1.52c compiler, the short while loop used no
279
** stack variables. The Watcom compiler, specified for use on NSPR 2.0,
280
** uses stack variables to implement the same while loop. This is
281
** a no-no! The copy operation clobbers these variables making the
282
** results of the copy ... unpredictable ... So, a short piece of
283
** inline assembly language is used to effect the copy.
285
** Following the restoration of the to-be-dispatched thread's
286
** stack context, another short inline piece of assemble language
287
** is used to set the SP register to correspond to what it was
288
** when the to-be-dispatched thread was suspended. This value
289
** uses the thread's stack->md.cxByteCount as a negative offset
290
** from _pr_top_of_task_stack as the new value of SP.
292
** Finally, Function Throw() is called to restore the architectural
293
** context of the to-be-dispatched thread.
295
** At this point, the newly dispatched thread appears to resume
296
** execution following the _PR_MD_SWITCH_CONTEXT() macro.
298
** OK, this ain't rocket-science, but it can confuse you easily.
299
** If you have to work on this stuff, please take the time to
300
** draw, on paper, the structures (PRThread, PRThreadStack,
301
** PRSegment, the "shadow stack", the system stack and the related
302
** global variables). Hand step it thru the debugger to make sure
303
** you understand it very well before making any changes. ...
307
void _MD_RESTORE_CONTEXT(PRThread *t)
312
** This is a good opportunity to make sure that the main
313
** mozilla thread actually gets some time. If interrupts
314
** are on, then we know it is safe to check if the main
315
** thread is being starved. If moz has not been scheduled
316
** for a long time, then then temporarily bump the fe priority
317
** up so that it gets to run at least one.
319
// #if 0 // lth. condition off for debug.
320
if (_pr_primordialThread == t) {
321
if (OldPriorityOfPrimaryThread != -1) {
322
PR_SetThreadPriority(_pr_primordialThread, OldPriorityOfPrimaryThread);
323
OldPriorityOfPrimaryThread = -1;
325
TimeSlicesOnNonPrimaryThread = 0;
327
TimeSlicesOnNonPrimaryThread++;
330
if ((TimeSlicesOnNonPrimaryThread >= 20) && (OldPriorityOfPrimaryThread == -1)) {
331
OldPriorityOfPrimaryThread = PR_GetThreadPriority(_pr_primordialThread);
332
PR_SetThreadPriority(_pr_primordialThread, 31);
333
TimeSlicesOnNonPrimaryThread = 0;
337
** Save the Task Stack into the "shadow stack" of the current thread
339
cxByteCount = (int) ((PRUint32) _pr_top_of_task_stack - (PRUint32) &t );
340
pSource = (char *) &t;
341
pTarget = (char *)((PRUint32)_pr_currentThread->stack->md.stackTop
342
- (PRUint32)cxByteCount );
343
_pr_currentThread->stack->md.cxByteCount = cxByteCount;
345
for( bytesMoved = 0; bytesMoved < cxByteCount; bytesMoved++ )
346
*(pTarget + bytesMoved ) = *(pSource + bytesMoved );
348
/* Mark the new thread as the current thread */
349
_pr_currentThread = t;
352
** Now copy the "shadow stack" of the new thread into the Task Stack
355
** After the stack has been copied, ALL local variables in this function
358
cxByteCount = t->stack->md.cxByteCount;
359
pSource = t->stack->md.stackTop - cxByteCount;
360
pTarget = _pr_top_of_task_stack - cxByteCount;
362
errno = (_pr_currentThread)->md.errcode;
367
mov si, WORD PTR [pSource]
368
mov di, WORD PTR [pTarget]
369
mov ax, WORD PTR [pTarget + 2]
371
mov ax, WORD PTR [pSource + 2]
381
** SS:SP is now invalid :-( This means that all local variables and
382
** function arguments are invalid and NO function calls can be
383
** made !!! We must fix up SS:SP so that function calls can safely
388
mov ax, WORD PTR [_pr_top_of_task_stack]
394
** Resume execution of thread: t by restoring the thread's context.
397
Throw((_pr_currentThread)->md.context, 1);
398
} /* --- end MD_RESTORE_CONTEXT() --- */
401
static void TraceDispatch( PRThread *thread )
406
** push all DispatchTrace objects to down one slot.
407
** Note: the last entry is lost; last-1 becomes last, etc.
409
for( i = NUM_DISPATCHTRACE_OBJECTS -2; i >= 0; i-- )
415
** Build dt[0] from t
418
dt->state = thread->state;
419
dt->mdThreadNumber = thread->md.threadNumber;
420
dt->priority = thread->priority;
423
} /* --- end TraceDispatch() --- */
426
/* $$ end W16thred.c */