home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /*
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- #include "primpl.h"
- #include <sys/timeb.h>
- #include <stdio.h>
-
- /*
- ** DispatchTrace -- define a thread dispatch trace entry
- **
- ** The DispatchTrace oject(s) are instantiated in a single
- ** array. Think of the array as a push-down stack; entry
- ** zero is the most recent, entry one the next most recent, etc.
- ** For each time PR_MD_RESTORE_CONTEXT() is called, the array
- ** is Pushed down and entry zero is overwritten with data
- ** for the newly dispatched thread.
- **
- ** Function TraceDispatch() manages the DispatchTrace array.
- **
- */
- typedef struct DispatchTrace
- {
- PRThread * thread;
- PRUint32 state;
- PRInt16 mdThreadNumber;
- PRInt16 unused;
- PRThreadPriority priority;
-
- } DispatchTrace, *DispatchTracePtr ;
-
- static void TraceDispatch( PRThread *thread );
-
-
- PRThread *_pr_primordialThread;
-
- /*
- ** Note: the static variables must be on the data-segment because
- ** the stack is destroyed during shadow-stack copy operations.
- **
- */
- static char * pSource; /* ptr to sourc of a "shadow-stack" copy */
- static char * pTarget; /* ptr to target of a "shadow-stack" copy */
- static int cxByteCount; /* number of bytes for "shadow-stack" copy */
- static int bytesMoved; /* instrumentation: WRT "shadow-stack" copy */
- static FILE * file1 = 0; /* instrumentation: WRT debug */
-
- #define NUM_DISPATCHTRACE_OBJECTS 24
- static DispatchTrace dt[NUM_DISPATCHTRACE_OBJECTS] = {0}; /* instrumentation: WRT dispatch */
- static PRUint32 dispatchCount = 0; /* instrumentation: number of thread dispatches */
-
- static int OldPriorityOfPrimaryThread = -1;
- static int TimeSlicesOnNonPrimaryThread = 0;
- static PRUint32 threadNumber = 1; /* Instrumentation: monotonically increasing number */
-
-
-
- /*
- ** _PR_MD_FINAL_INIT() -- Final MD Initialization
- **
- ** Poultry Problems! ... The stack, as allocated by PR_NewStack()
- ** is called from here, late in initialization, because PR_NewStack()
- ** requires lots of things working. When some elements of the
- ** primordial thread are created, early in initialization, the
- ** shadow stack is not one of these things. The "shadow stack" is
- ** created here, late in initiailization using PR_NewStack(), to
- ** ensure consistency in creation of the related objects.
- **
- ** A new ThreadStack, and all its affiliated structures, is allocated
- ** via the call to PR_NewStack(). The PRThread structure in the
- ** new stack is ignored; the old PRThread structure is used (why?).
- ** The old PRThreadStack structure is abandoned.
- **
- */
- void
- _PR_MD_FINAL_INIT()
- {
- PRThreadStack * stack = 0;
- PRInt32 stacksize = 0;
- PRThread * me = _PR_MD_CURRENT_THREAD();
-
- _PR_ADJUST_STACKSIZE( stacksize );
- stack = _PR_NewStack( stacksize );
-
- me->stack = stack;
- stack->thr = me;
-
- return;
- } /* --- end _PR_MD_FINAL_INIT() --- */
-
-
- void
- _MD_INIT_RUNNING_CPU( struct _PRCPU *cpu )
- {
- PR_INIT_CLIST(&(cpu->md.ioQ));
- cpu->md.ioq_max_osfd = -1;
- cpu->md.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
- }
-
-
- void
- _PR_MD_YIELD( void )
- {
- PR_ASSERT(0);
- }
-
- /*
- ** _PR_MD_INIT_STACK() -- Win16 specific Stack initialization.
- **
- **
- */
-
- void
- _PR_MD_INIT_STACK( PRThreadStack *ts, PRIntn redzone )
- {
- ts->md.stackTop = ts->stackTop - sizeof(PRThread);
- ts->md.cxByteCount = 0;
-
- return;
- } /* --- end _PR_MD_INIT_STACK() --- */
-
- /*
- ** _PR_MD_INIT_THREAD() -- Win16 specific Thread initialization.
- **
- */
- PRStatus
- _PR_MD_INIT_THREAD(PRThread *thread)
- {
- if ( thread->flags & _PR_PRIMORDIAL)
- {
- _pr_primordialThread = thread;
- thread->md.threadNumber = 1;
- }
- else
- {
- thread->md.threadNumber = ++threadNumber;
- }
-
- thread->md.magic = _MD_MAGIC_THREAD;
- strcpy( thread->md.guardBand, "GuardBand" );
-
- return PR_SUCCESS;
- }
-
-
- PRStatus
- _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
- {
- _MD_SWITCH_CONTEXT( thread );
-
- return( PR_SUCCESS );
- }
-
- void *PR_W16GetExceptionContext(void)
- {
- return _MD_CURRENT_THREAD()->md.exceptionContext;
- }
-
- void
- PR_W16SetExceptionContext(void *context)
- {
- _MD_CURRENT_THREAD()->md.exceptionContext = context;
- }
-
-
-
-
- /*
- ** _MD_RESTORE_CONTEXT() -- Resume execution of thread 't'.
- **
- ** Win16 threading is based on the NSPR 2.0 general model of
- ** user threads. It differs from the general model in that a
- ** single "real" stack segment is used for execution of all
- ** threads. The context of the suspended threads is preserved
- ** in the md.context [and related members] of the PRThread
- ** structure. The stack context of the suspended thread is
- ** preserved in a "shadow stack" object.
- **
- ** _MD_RESTORE_CONTEXT() implements most of the thread switching
- ** for NSPR's implementation of Win16 theads.
- **
- ** Operations Notes:
- **
- ** Function PR_NewStack() in prustack.c allocates a new
- ** PRThreadStack, PRStack, PRSegment, and a "shadow" stack
- ** for a thread. These structures are wired together to
- ** form the basis of Win16 threads. The thread and shadow
- ** stack structures are created as part of PR_CreateThread().
- **
- ** Note! Some special "magic" is applied to the "primordial"
- ** thread. The physical layout of the PRThread, PRThreadStack,
- ** shadow stack, ... is somewhat different. Watch yourself when
- ** mucking around with it. ... See _PR_MD_FINAL_INIT() for most
- ** of the special treatment of the primordial thread.
- **
- ** Function _PR_MD_INIT_STACK() initializes the value of
- ** PRThreadStack member md.cxByteCount to zero; there
- ** is no context to be restored for a thread's initial
- ** dispatch. The value of member md.stackTop is set to
- ** point to the highest usable address on the shadow stack.
- ** This point corresponds to _pr_top_of_task_stack on the
- ** system's operating stack.
- **
- ** _pr_top_of_task_stack points to a place on the system stack
- ** considered to be "close to the top". Stack context is preserved
- ** relative to this point.
- **
- ** Reminder: In x86 architecture, the stack grows "down".
- ** That is: the stack pointer (SP register) is decremented
- ** to push objects onto the stack or when a call is made.
- **
- ** Function _PR_MD_WAIT() invokes macro _MD_SWITCH_CONTEXT();
- ** this causes the hardware registers to be preserved in a
- ** CATCHBUF structure using function Catch() [see _win16.h],
- ** then calls PR_Schedule() to select a new thread for dispatch.
- ** PR_Schedule() calls _MD_RESTORE_CONTEXT() to cause the thread
- ** being suspended's stack to be preserved, to restore the
- ** stack of the to-be-dispactched thread, and to restore the
- ** to-be-dispactched thread's hardware registers.
- **
- ** At the moment _PR_MD_RESTORE_CONTEXT() is called, the stack
- ** pointer (SP) is less than the reference pointer
- ** _pr_top_of_task_stack. The distance difference between the SP and
- ** _pr_top_of_task_stack is the amount of stack that must be preserved.
- ** This value, cxByteCount, is calculated then preserved in the
- ** PRThreadStack.md.cxByteCount for later use (size of stack
- ** context to restore) when this thread is dispatched again.
- **
- ** A C language for() loop is used to copy, byte-by-byte, the
- ** stack data being preserved starting at the "address of t"
- ** [Note: 't' is the argument passed to _PR_MD_RESTORE_CONTEXT()]
- ** for the length of cxByteCount.
- **
- ** variables pSource and pTarget are the calculated source and
- ** destination pointers for the stack copy operation. These
- ** variables are static scope because they cannot be instantiated
- ** on the stack itself, since the stack is clobbered by restoring
- ** the to-be-dispatched thread's stack context.
- **
- ** After preserving the suspended thread's stack and architectural
- ** context, the to-be-dispatched thread's stack context is copied
- ** from its shadow stack to the system operational stack. The copy
- ** is done in a small fragment of in-line assembly language. Note:
- ** In NSPR 1.0, a while() loop was used to do the copy; when compiled
- ** with the MS C 1.52c compiler, the short while loop used no
- ** stack variables. The Watcom compiler, specified for use on NSPR 2.0,
- ** uses stack variables to implement the same while loop. This is
- ** a no-no! The copy operation clobbers these variables making the
- ** results of the copy ... unpredictable ... So, a short piece of
- ** inline assembly language is used to effect the copy.
- **
- ** Following the restoration of the to-be-dispatched thread's
- ** stack context, another short inline piece of assemble language
- ** is used to set the SP register to correspond to what it was
- ** when the to-be-dispatched thread was suspended. This value
- ** uses the thread's stack->md.cxByteCount as a negative offset
- ** from _pr_top_of_task_stack as the new value of SP.
- **
- ** Finally, Function Throw() is called to restore the architectural
- ** context of the to-be-dispatched thread.
- **
- ** At this point, the newly dispatched thread appears to resume
- ** execution following the _PR_MD_SWITCH_CONTEXT() macro.
- **
- ** OK, this ain't rocket-science, but it can confuse you easily.
- ** If you have to work on this stuff, please take the time to
- ** draw, on paper, the structures (PRThread, PRThreadStack,
- ** PRSegment, the "shadow stack", the system stack and the related
- ** global variables). Hand step it thru the debugger to make sure
- ** you understand it very well before making any changes. ...
- ** YMMV.
- **
- */
- void _MD_RESTORE_CONTEXT(PRThread *t)
- {
- dispatchCount++;
- TraceDispatch( t );
- /*
- ** This is a good opportunity to make sure that the main
- ** mozilla thread actually gets some time. If interrupts
- ** are on, then we know it is safe to check if the main
- ** thread is being starved. If moz has not been scheduled
- ** for a long time, then then temporarily bump the fe priority
- ** up so that it gets to run at least one.
- */
- // #if 0 // lth. condition off for debug.
- if (_pr_primordialThread == t) {
- if (OldPriorityOfPrimaryThread != -1) {
- PR_SetThreadPriority(_pr_primordialThread, OldPriorityOfPrimaryThread);
- OldPriorityOfPrimaryThread = -1;
- }
- TimeSlicesOnNonPrimaryThread = 0;
- } else {
- TimeSlicesOnNonPrimaryThread++;
- }
-
- if ((TimeSlicesOnNonPrimaryThread >= 20) && (OldPriorityOfPrimaryThread == -1)) {
- OldPriorityOfPrimaryThread = PR_GetThreadPriority(_pr_primordialThread);
- PR_SetThreadPriority(_pr_primordialThread, 31);
- TimeSlicesOnNonPrimaryThread = 0;
- }
- // #endif
- /*
- ** Save the Task Stack into the "shadow stack" of the current thread
- */
- cxByteCount = (int) ((PRUint32) _pr_top_of_task_stack - (PRUint32) &t );
- pSource = (char *) &t;
- pTarget = (char *)((PRUint32)_pr_currentThread->stack->md.stackTop
- - (PRUint32)cxByteCount );
- _pr_currentThread->stack->md.cxByteCount = cxByteCount;
-
- for( bytesMoved = 0; bytesMoved < cxByteCount; bytesMoved++ )
- *(pTarget + bytesMoved ) = *(pSource + bytesMoved );
-
- /* Mark the new thread as the current thread */
- _pr_currentThread = t;
-
- /*
- ** Now copy the "shadow stack" of the new thread into the Task Stack
- **
- ** REMEMBER:
- ** After the stack has been copied, ALL local variables in this function
- ** are invalid !!
- */
- cxByteCount = t->stack->md.cxByteCount;
- pSource = t->stack->md.stackTop - cxByteCount;
- pTarget = _pr_top_of_task_stack - cxByteCount;
-
- errno = (_pr_currentThread)->md.errcode;
-
- __asm
- {
- mov cx, cxByteCount
- mov si, WORD PTR [pSource]
- mov di, WORD PTR [pTarget]
- mov ax, WORD PTR [pTarget + 2]
- mov es, ax
- mov ax, WORD PTR [pSource + 2]
- mov bx, ds
- mov ds, ax
- rep movsb
- mov ds, bx
- }
-
- /*
- ** IMPORTANT:
- ** ----------
- ** SS:SP is now invalid :-( This means that all local variables and
- ** function arguments are invalid and NO function calls can be
- ** made !!! We must fix up SS:SP so that function calls can safely
- ** be made...
- */
-
- __asm {
- mov ax, WORD PTR [_pr_top_of_task_stack]
- sub ax, cxByteCount
- mov sp, ax
- };
-
- /*
- ** Resume execution of thread: t by restoring the thread's context.
- **
- */
- Throw((_pr_currentThread)->md.context, 1);
- } /* --- end MD_RESTORE_CONTEXT() --- */
-
-
- static void TraceDispatch( PRThread *thread )
- {
- int i;
-
- /*
- ** push all DispatchTrace objects to down one slot.
- ** Note: the last entry is lost; last-1 becomes last, etc.
- */
- for( i = NUM_DISPATCHTRACE_OBJECTS -2; i >= 0; i-- )
- {
- dt[i +1] = dt[i];
- }
-
- /*
- ** Build dt[0] from t
- */
- dt->thread = thread;
- dt->state = thread->state;
- dt->mdThreadNumber = thread->md.threadNumber;
- dt->priority = thread->priority;
-
- return;
- } /* --- end TraceDispatch() --- */
-
-
- /* $$ end W16thred.c */
-