Back to index

lightning-sunbird  0.9+nobinonly
w16thred.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "primpl.h"
00039 #include <sys/timeb.h>
00040 #include <stdio.h>
00041 
00042 /*
00043 ** DispatchTrace -- define a thread dispatch trace entry
00044 **
00045 ** The DispatchTrace oject(s) are instantiated in a single
00046 ** array. Think of the array as a push-down stack; entry
00047 ** zero is the most recent, entry one the next most recent, etc.
00048 ** For each time PR_MD_RESTORE_CONTEXT() is called, the array
00049 ** is Pushed down and entry zero is overwritten with data
00050 ** for the newly dispatched thread.
00051 **
00052 ** Function TraceDispatch() manages the DispatchTrace array.
00053 **
00054 */
00055 typedef struct DispatchTrace
00056 {
00057     PRThread *          thread;
00058     PRUint32            state;
00059     PRInt16             mdThreadNumber;
00060     PRInt16             unused;
00061     PRThreadPriority    priority;
00062     
00063 } DispatchTrace, *DispatchTracePtr ;
00064 
00065 static void TraceDispatch( PRThread *thread );
00066 
00067 
00068 PRThread                *_pr_primordialThread;
00069 
00070 /*
00071 ** Note: the static variables must be on the data-segment because
00072 ** the stack is destroyed during shadow-stack copy operations.
00073 **
00074 */
00075 static char * pSource;          /* ptr to sourc of a "shadow-stack" copy */
00076 static char * pTarget;          /* ptr to target of a "shadow-stack" copy */
00077 static int   cxByteCount;       /* number of bytes for "shadow-stack" copy */
00078 static int   bytesMoved;        /* instrumentation: WRT "shadow-stack" copy */
00079 static FILE *    file1 = 0;     /* instrumentation: WRT debug */
00080 
00081 #define NUM_DISPATCHTRACE_OBJECTS  24
00082 static DispatchTrace dt[NUM_DISPATCHTRACE_OBJECTS] = {0}; /* instrumentation: WRT dispatch */
00083 static PRUint32 dispatchCount = 0;  /* instrumentation: number of thread dispatches */
00084 
00085 static int OldPriorityOfPrimaryThread   = -1;
00086 static int TimeSlicesOnNonPrimaryThread =  0;
00087 static PRUint32 threadNumber = 1;   /* Instrumentation: monotonically increasing number */
00088 
00089 
00090 
00091 /*
00092 ** _PR_MD_FINAL_INIT() -- Final MD Initialization
00093 **
00094 ** Poultry Problems! ... The stack, as allocated by PR_NewStack()
00095 ** is called from here, late in initialization, because PR_NewStack()
00096 ** requires lots of things working. When some elements of the
00097 ** primordial thread are created, early in initialization, the
00098 ** shadow stack is not one of these things. The "shadow stack" is
00099 ** created here, late in initiailization using PR_NewStack(), to
00100 ** ensure consistency in creation of the related objects.
00101 ** 
00102 ** A new ThreadStack, and all its affiliated structures, is allocated
00103 ** via the call to PR_NewStack(). The PRThread structure in the
00104 ** new stack is ignored; the old PRThread structure is used (why?).
00105 ** The old PRThreadStack structure is abandoned.
00106 **
00107 */
00108 void
00109 _PR_MD_FINAL_INIT()
00110 {
00111     PRThreadStack *     stack = 0;
00112     PRInt32             stacksize = 0;
00113     PRThread *          me = _PR_MD_CURRENT_THREAD();
00114     
00115     _PR_ADJUST_STACKSIZE( stacksize );
00116     stack = _PR_NewStack( stacksize );
00117     
00118     me->stack = stack;
00119     stack->thr = me;
00120     
00121     return;
00122 } /* --- end _PR_MD_FINAL_INIT() --- */
00123 
00124 
00125 void
00126 _MD_INIT_RUNNING_CPU( struct _PRCPU *cpu )
00127 {
00128        PR_INIT_CLIST(&(cpu->md.ioQ));
00129        cpu->md.ioq_max_osfd = -1;
00130        cpu->md.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
00131 }    
00132 
00133 
00134 void
00135 _PR_MD_YIELD( void )
00136 {
00137     PR_ASSERT(0);
00138 }
00139 
00140 /*
00141 ** _PR_MD_INIT_STACK() -- Win16 specific Stack initialization.
00142 **
00143 **
00144 */
00145 
00146 void
00147 _PR_MD_INIT_STACK( PRThreadStack *ts, PRIntn redzone )
00148 {
00149     ts->md.stackTop = ts->stackTop - sizeof(PRThread);
00150     ts->md.cxByteCount = 0;
00151     
00152     return;
00153 } /* --- end _PR_MD_INIT_STACK() --- */
00154 
00155 /*
00156 **  _PR_MD_INIT_THREAD() -- Win16 specific Thread initialization.
00157 **
00158 */
00159 PRStatus
00160 _PR_MD_INIT_THREAD(PRThread *thread)
00161 {
00162     if ( thread->flags & _PR_PRIMORDIAL)
00163     {
00164         _pr_primordialThread = thread;
00165         thread->md.threadNumber = 1;
00166     }
00167     else
00168     {
00169         thread->md.threadNumber = ++threadNumber;
00170     }
00171 
00172     thread->md.magic = _MD_MAGIC_THREAD;
00173     strcpy( thread->md.guardBand, "GuardBand" );
00174     
00175     return PR_SUCCESS;
00176 }
00177 
00178 
00179 PRStatus
00180 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
00181 {
00182     _MD_SWITCH_CONTEXT( thread );
00183     
00184     return( PR_SUCCESS );
00185 }
00186 
00187 void *PR_W16GetExceptionContext(void)
00188 {
00189     return _MD_CURRENT_THREAD()->md.exceptionContext;
00190 }
00191 
00192 void
00193 PR_W16SetExceptionContext(void *context)
00194 {
00195     _MD_CURRENT_THREAD()->md.exceptionContext = context;
00196 }
00197 
00198 
00199 
00200 
00201 /*
00202 ** _MD_RESTORE_CONTEXT() -- Resume execution of thread 't'.
00203 **
00204 ** Win16 threading is based on the NSPR 2.0 general model of
00205 ** user threads. It differs from the general model in that a 
00206 ** single "real" stack segment is used for execution of all 
00207 ** threads. The context of the suspended threads is preserved
00208 ** in the md.context [and related members] of the PRThread 
00209 ** structure. The stack context of the suspended thread is
00210 ** preserved in a "shadow stack" object.
00211 **
00212 ** _MD_RESTORE_CONTEXT() implements most of the thread switching
00213 ** for NSPR's implementation of Win16 theads.
00214 **
00215 ** Operations Notes:
00216 **
00217 ** Function PR_NewStack() in prustack.c allocates a new
00218 ** PRThreadStack, PRStack, PRSegment, and a "shadow" stack
00219 ** for a thread. These structures are wired together to
00220 ** form the basis of Win16 threads. The thread and shadow
00221 ** stack structures are created as part of PR_CreateThread().
00222 ** 
00223 ** Note! Some special "magic" is applied to the "primordial"
00224 ** thread. The physical layout of the PRThread, PRThreadStack,
00225 ** shadow stack, ... is somewhat different. Watch yourself when
00226 ** mucking around with it. ... See _PR_MD_FINAL_INIT() for most
00227 ** of the special treatment of the primordial thread.
00228 **
00229 ** Function _PR_MD_INIT_STACK() initializes the value of
00230 ** PRThreadStack member md.cxByteCount to zero; there
00231 ** is no context to be restored for a thread's initial
00232 ** dispatch. The value of member md.stackTop is set to
00233 ** point to the highest usable address on the shadow stack.
00234 ** This point corresponds to _pr_top_of_task_stack on the
00235 ** system's operating stack.
00236 **
00237 ** _pr_top_of_task_stack points to a place on the system stack
00238 ** considered to be "close to the top". Stack context is preserved
00239 ** relative to this point.
00240 **
00241 ** Reminder: In x86 architecture, the stack grows "down".
00242 ** That is: the stack pointer (SP register) is decremented
00243 ** to push objects onto the stack or when a call is made.
00244 ** 
00245 ** Function _PR_MD_WAIT() invokes macro _MD_SWITCH_CONTEXT();
00246 ** this causes the hardware registers to be preserved in a
00247 ** CATCHBUF structure using function Catch() [see _win16.h], 
00248 ** then calls PR_Schedule() to select a new thread for dispatch. 
00249 ** PR_Schedule() calls _MD_RESTORE_CONTEXT() to cause the thread 
00250 ** being suspended's stack to be preserved, to restore the 
00251 ** stack of the to-be-dispactched thread, and to restore the 
00252 ** to-be-dispactched thread's hardware registers.
00253 **
00254 ** At the moment _PR_MD_RESTORE_CONTEXT() is called, the stack
00255 ** pointer (SP) is less than the reference pointer
00256 ** _pr_top_of_task_stack. The distance difference between the SP and
00257 ** _pr_top_of_task_stack is the amount of stack that must be preserved.
00258 ** This value, cxByteCount, is calculated then preserved in the
00259 ** PRThreadStack.md.cxByteCount for later use (size of stack
00260 ** context to restore) when this thread is dispatched again.
00261 ** 
00262 ** A C language for() loop is used to copy, byte-by-byte, the
00263 ** stack data being preserved starting at the "address of t"
00264 ** [Note: 't' is the argument passed to _PR_MD_RESTORE_CONTEXT()]
00265 ** for the length of cxByteCount.
00266 **
00267 ** variables pSource and pTarget are the calculated source and
00268 ** destination pointers for the stack copy operation. These
00269 ** variables are static scope because they cannot be instantiated
00270 ** on the stack itself, since the stack is clobbered by restoring
00271 ** the to-be-dispatched thread's stack context.
00272 **
00273 ** After preserving the suspended thread's stack and architectural
00274 ** context, the to-be-dispatched thread's stack context is copied
00275 ** from its shadow stack to the system operational stack. The copy
00276 ** is done in a small fragment of in-line assembly language. Note:
00277 ** In NSPR 1.0, a while() loop was used to do the copy; when compiled
00278 ** with the MS C 1.52c compiler, the short while loop used no
00279 ** stack variables. The Watcom compiler, specified for use on NSPR 2.0,
00280 ** uses stack variables to implement the same while loop. This is
00281 ** a no-no! The copy operation clobbers these variables making the
00282 ** results of the copy ... unpredictable ... So, a short piece of
00283 ** inline assembly language is used to effect the copy.
00284 **
00285 ** Following the restoration of the to-be-dispatched thread's
00286 ** stack context, another short inline piece of assemble language
00287 ** is used to set the SP register to correspond to what it was
00288 ** when the to-be-dispatched thread was suspended. This value
00289 ** uses the thread's stack->md.cxByteCount as a negative offset 
00290 ** from _pr_top_of_task_stack as the new value of SP.
00291 **
00292 ** Finally, Function Throw() is called to restore the architectural
00293 ** context of the to-be-dispatched thread.
00294 **
00295 ** At this point, the newly dispatched thread appears to resume
00296 ** execution following the _PR_MD_SWITCH_CONTEXT() macro.
00297 **
00298 ** OK, this ain't rocket-science, but it can confuse you easily.
00299 ** If you have to work on this stuff, please take the time to
00300 ** draw, on paper, the structures (PRThread, PRThreadStack,
00301 ** PRSegment, the "shadow stack", the system stack and the related
00302 ** global variables). Hand step it thru the debugger to make sure
00303 ** you understand it very well before making any changes. ...
00304 ** YMMV.
00305 ** 
00306 */
00307 void _MD_RESTORE_CONTEXT(PRThread *t)
00308 {
00309     dispatchCount++;
00310     TraceDispatch( t );
00311     /* 
00312     ** This is a good opportunity to make sure that the main
00313     ** mozilla thread actually gets some time.  If interrupts
00314     ** are on, then we know it is safe to check if the main
00315     ** thread is being starved.  If moz has not been scheduled
00316     ** for a long time, then then temporarily bump the fe priority 
00317     ** up so that it gets to run at least one. 
00318     */ 
00319 // #if 0 // lth. condition off for debug.
00320     if (_pr_primordialThread == t) {
00321         if (OldPriorityOfPrimaryThread != -1) {
00322             PR_SetThreadPriority(_pr_primordialThread, OldPriorityOfPrimaryThread);
00323             OldPriorityOfPrimaryThread = -1;
00324         }
00325         TimeSlicesOnNonPrimaryThread = 0;
00326     } else {
00327         TimeSlicesOnNonPrimaryThread++;
00328     }
00329 
00330     if ((TimeSlicesOnNonPrimaryThread >= 20) && (OldPriorityOfPrimaryThread == -1)) {
00331         OldPriorityOfPrimaryThread = PR_GetThreadPriority(_pr_primordialThread);
00332         PR_SetThreadPriority(_pr_primordialThread, 31);
00333         TimeSlicesOnNonPrimaryThread = 0;
00334     }
00335 // #endif
00336     /*
00337     ** Save the Task Stack into the "shadow stack" of the current thread
00338     */
00339     cxByteCount  = (int) ((PRUint32) _pr_top_of_task_stack - (PRUint32) &t );
00340     pSource      = (char *) &t;
00341     pTarget      = (char *)((PRUint32)_pr_currentThread->stack->md.stackTop 
00342                             - (PRUint32)cxByteCount );
00343     _pr_currentThread->stack->md.cxByteCount = cxByteCount;
00344     
00345     for( bytesMoved = 0; bytesMoved < cxByteCount; bytesMoved++ )
00346         *(pTarget + bytesMoved ) = *(pSource + bytesMoved );
00347     
00348     /* Mark the new thread as the current thread */
00349     _pr_currentThread = t;
00350 
00351     /*
00352     ** Now copy the "shadow stack" of the new thread into the Task Stack
00353     **
00354     ** REMEMBER:
00355     **    After the stack has been copied, ALL local variables in this function
00356     **    are invalid !!
00357     */
00358     cxByteCount  = t->stack->md.cxByteCount;
00359     pSource      = t->stack->md.stackTop - cxByteCount;
00360     pTarget      = _pr_top_of_task_stack - cxByteCount;
00361     
00362     errno = (_pr_currentThread)->md.errcode;
00363     
00364     __asm 
00365     {
00366         mov cx, cxByteCount
00367         mov si, WORD PTR [pSource]
00368         mov di, WORD PTR [pTarget]
00369         mov ax, WORD PTR [pTarget + 2]
00370         mov es, ax
00371         mov ax, WORD PTR [pSource + 2]
00372         mov bx, ds
00373         mov ds, ax
00374         rep movsb
00375         mov ds, bx
00376     }
00377 
00378     /* 
00379     ** IMPORTANT:
00380     ** ----------
00381     ** SS:SP is now invalid :-( This means that all local variables and
00382     ** function arguments are invalid and NO function calls can be
00383     ** made !!! We must fix up SS:SP so that function calls can safely
00384     ** be made...
00385     */
00386 
00387     __asm {
00388         mov     ax, WORD PTR [_pr_top_of_task_stack]
00389         sub     ax, cxByteCount
00390         mov     sp, ax
00391     };
00392 
00393     /*
00394     ** Resume execution of thread: t by restoring the thread's context.
00395     **
00396     */
00397     Throw((_pr_currentThread)->md.context, 1);
00398 } /* --- end MD_RESTORE_CONTEXT() --- */
00399 
00400 
00401 static void TraceDispatch( PRThread *thread )
00402 {
00403     int i;
00404     
00405     /*
00406     ** push all DispatchTrace objects to down one slot.
00407     ** Note: the last entry is lost; last-1 becomes last, etc.
00408     */
00409     for( i = NUM_DISPATCHTRACE_OBJECTS -2; i >= 0; i-- )
00410     {
00411         dt[i +1] = dt[i];
00412     }
00413     
00414     /*
00415     ** Build dt[0] from t
00416     */
00417     dt->thread = thread;
00418     dt->state = thread->state;
00419     dt->mdThreadNumber = thread->md.threadNumber;
00420     dt->priority = thread->priority;
00421     
00422     return;
00423 } /* --- end TraceDispatch() --- */
00424 
00425 
00426 /* $$ end W16thred.c */