Back to index

lightning-sunbird  0.9+nobinonly
macthr.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 
00040 #include <string.h>
00041 
00042 #include <MacTypes.h>
00043 #include <Timer.h>
00044 #include <OSUtils.h>
00045 #include <Math64.h>
00046 #include <LowMem.h>
00047 #include <Multiprocessing.h>
00048 #include <Gestalt.h>
00049 
00050 #include "mdcriticalregion.h"
00051 
00052 TimerUPP      gTimerCallbackUPP    = NULL;
00053 PRThread *    gPrimaryThread              = NULL;
00054 
00055 ProcessSerialNumber         gApplicationProcess;
00056 
00057 PR_IMPLEMENT(PRThread *) PR_GetPrimaryThread()
00058 {
00059        return gPrimaryThread;
00060 }
00061 
00062 //##############################################################################
00063 //##############################################################################
00064 #pragma mark -
00065 #pragma mark CREATING MACINTOSH THREAD STACKS
00066 
00067 #if defined(GC_LEAK_DETECTOR)
00068 extern void* GC_malloc_atomic(PRUint32 size);
00069 #endif
00070 
00071 /*
00072 **     Allocate a new memory segment.  We allocate it from our figment heap.  Currently,
00073 **     it is being used for per thread stack space.
00074 **     
00075 **     Return the segment's access rights and size.  vaddr is used on Unix platforms to
00076 **     map an existing address for the segment.
00077 */
00078 PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
00079 {
00080        PR_ASSERT(seg != 0);
00081        PR_ASSERT(size != 0);
00082        PR_ASSERT(vaddr == 0);
00083 
00084        /*     
00085        ** Take the actual memory for the segment out of our Figment heap.
00086        */
00087 
00088 #if defined(GC_LEAK_DETECTOR)
00089        seg->vaddr = (char *)GC_malloc_atomic(size);
00090 #else
00091        seg->vaddr = (char *)malloc(size);
00092 #endif
00093 
00094        if (seg->vaddr == NULL) {
00095 
00096 #if DEBUG
00097               DebugStr("\p_MD_AllocSegment failed.");
00098 #endif
00099 
00100               return PR_FAILURE;
00101        }
00102 
00103        seg->size = size;    
00104 
00105        return PR_SUCCESS;
00106 }
00107 
00108 
00109 /*
00110 **     Free previously allocated memory segment.
00111 */
00112 void _MD_FreeSegment(PRSegment *seg)
00113 {
00114        PR_ASSERT((seg->flags & _PR_SEG_VM) == 0);
00115 
00116        if (seg->vaddr != NULL)
00117               free(seg->vaddr);
00118 }
00119 
00120 
00121 /*
00122 **     The thread's stack has been allocated and its fields are already properly filled
00123 **     in by PR.  Perform any debugging related initialization here.
00124 **
00125 **     Put a recognizable pattern so that we can find it from Macsbug.
00126 **     Put a cookie at the top of the stack so that we can find it from Macsbug.
00127 */
00128 void _MD_InitStack(PRThreadStack *ts, int redZoneBytes)
00129        {
00130 #pragma unused (redZoneBytes)
00131 #if DEVELOPER_DEBUG
00132        //     Put a cookie at the top of the stack so that we can find 
00133        //     it from Macsbug.
00134        
00135        memset(ts->allocBase, 0xDC, ts->stackSize);
00136        
00137        ((UInt32 *)ts->stackTop)[-1] = 0xBEEFCAFE;
00138        ((UInt32 *)ts->stackTop)[-2] = (UInt32)gPrimaryThread;
00139        ((UInt32 *)ts->stackTop)[-3] = (UInt32)(ts);
00140        ((UInt32 *)ts->stackBottom)[0] = 0xCAFEBEEF;
00141 #else
00142 #pragma unused (ts)
00143 #endif 
00144        }
00145 
00146 extern void _MD_ClearStack(PRThreadStack *ts)
00147        {
00148 #if DEVELOPER_DEBUG
00149        //     Clear out our cookies. 
00150        
00151        memset(ts->allocBase, 0xEF, ts->allocSize);
00152        ((UInt32 *)ts->stackTop)[-1] = 0;
00153        ((UInt32 *)ts->stackTop)[-2] = 0;
00154        ((UInt32 *)ts->stackTop)[-3] = 0;
00155        ((UInt32 *)ts->stackBottom)[0] = 0;
00156 #else
00157 #pragma unused (ts)
00158 #endif
00159        }
00160 
00161 
00162 //##############################################################################
00163 //##############################################################################
00164 #pragma mark -
00165 #pragma mark TIME MANAGER-BASED CLOCK
00166 
00167 // On Mac OS X, it's possible for the application to spend lots of time
00168 // in WaitNextEvent, yielding to other applications. Since NSPR threads are
00169 // cooperative here, this means that NSPR threads will also get very little
00170 // time to run. To kick ourselves out of a WaitNextEvent call when we have
00171 // determined that it's time to schedule another thread, the Timer Task
00172 // (which fires every 8ms, even when other apps have the CPU) calls WakeUpProcess.
00173 // We only want to do this on Mac OS X; the gTimeManagerTaskDoesWUP variable
00174 // indicates when we're running on that OS.
00175 //
00176 // Note that the TimerCallback makes use of gApplicationProcess. We need to
00177 // have set this up before the first possible run of the timer task; we do
00178 // so in _MD_EarlyInit().
00179 static Boolean  gTimeManagerTaskDoesWUP;
00180 
00181 static TMTask   gTimeManagerTaskElem;
00182 
00183 extern void _MD_IOInterrupt(void);
00184 _PRInterruptTable _pr_interruptTable[] = {
00185     { "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, },
00186     { "i/o", _PR_MISSED_IO, _MD_IOInterrupt, },
00187     { 0 }
00188 };
00189 
00190 #define kMacTimerInMiliSecs 8L
00191 
00192 pascal void TimerCallback(TMTaskPtr tmTaskPtr)
00193 {
00194     _PRCPU *cpu = _PR_MD_CURRENT_CPU();
00195     PRIntn is;
00196 
00197     if (_PR_MD_GET_INTSOFF()) {
00198         cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
00199         PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
00200         return;
00201     }
00202 
00203     _PR_INTSOFF(is);
00204 
00205     // And tell nspr that a clock interrupt occured.
00206     _PR_ClockInterrupt();
00207        
00208     if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority))) {
00209         if (gTimeManagerTaskDoesWUP) {
00210             // We only want to call WakeUpProcess if we know that NSPR has managed to switch threads
00211             // since the last call, otherwise we end up spewing out WakeUpProcess() calls while the
00212             // application is blocking somewhere. This can interfere with events loops other than
00213             // our own (see bug 158927).
00214             if (UnsignedWideToUInt64(cpu->md.lastThreadSwitch) > UnsignedWideToUInt64(cpu->md.lastWakeUpProcess))
00215             {
00216                 WakeUpProcess(&gApplicationProcess);
00217                 cpu->md.lastWakeUpProcess = UpTime();
00218             }
00219         }
00220         _PR_SET_RESCHED_FLAG();
00221        }
00222        
00223     _PR_FAST_INTSON(is);
00224 
00225     // Reset the clock timer so that we fire again.
00226     PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
00227 }
00228 
00229 
00230 void _MD_StartInterrupts(void)
00231 {
00232        gPrimaryThread = _PR_MD_CURRENT_THREAD();
00233 
00234        gTimeManagerTaskDoesWUP = RunningOnOSX();
00235 
00236        if ( !gTimerCallbackUPP )
00237               gTimerCallbackUPP = NewTimerUPP(TimerCallback);
00238 
00239        //     Fill in the Time Manager queue element
00240        
00241        gTimeManagerTaskElem.tmAddr = (TimerUPP)gTimerCallbackUPP;
00242        gTimeManagerTaskElem.tmCount = 0;
00243        gTimeManagerTaskElem.tmWakeUp = 0;
00244        gTimeManagerTaskElem.tmReserved = 0;
00245 
00246        //     Make sure that our time manager task is ready to go.
00247        InsTime((QElemPtr)&gTimeManagerTaskElem);
00248        
00249        PrimeTime((QElemPtr)&gTimeManagerTaskElem, kMacTimerInMiliSecs);
00250 }
00251 
00252 void _MD_StopInterrupts(void)
00253 {
00254        if (gTimeManagerTaskElem.tmAddr != NULL) {
00255               RmvTime((QElemPtr)&gTimeManagerTaskElem);
00256               gTimeManagerTaskElem.tmAddr = NULL;
00257        }
00258 }
00259 
00260 
00261 #define MAX_PAUSE_TIMEOUT_MS    500
00262 
00263 void _MD_PauseCPU(PRIntervalTime timeout)
00264 {
00265     if (timeout != PR_INTERVAL_NO_WAIT)
00266     {
00267         // There is a race condition entering the critical section
00268         // in AsyncIOCompletion (and probably elsewhere) that can
00269         // causes deadlock for the duration of this timeout. To
00270         // work around this, use a max 500ms timeout for now.
00271         // See bug 99561 for details.
00272         if (PR_IntervalToMilliseconds(timeout) > MAX_PAUSE_TIMEOUT_MS)
00273             timeout = PR_MillisecondsToInterval(MAX_PAUSE_TIMEOUT_MS);
00274 
00275         WaitOnIdleSemaphore(timeout);
00276         (void) _MD_IOInterrupt();
00277     }
00278 }
00279 
00280 void _MD_InitRunningCPU(_PRCPU* cpu)
00281 {
00282     cpu->md.trackScheduling = RunningOnOSX();
00283     if (cpu->md.trackScheduling) {
00284         AbsoluteTime    zeroTime = {0, 0};
00285         cpu->md.lastThreadSwitch = UpTime();
00286         cpu->md.lastWakeUpProcess = zeroTime;
00287     }
00288 }
00289 
00290 
00291 //##############################################################################
00292 //##############################################################################
00293 #pragma mark -
00294 #pragma mark THREAD SUPPORT FUNCTIONS
00295 
00296 #include <OpenTransport.h> /* for error codes */
00297 
00298 PRStatus _MD_InitThread(PRThread *thread)
00299 {
00300        thread->md.asyncIOLock = PR_NewLock();
00301        PR_ASSERT(thread->md.asyncIOLock != NULL);
00302        thread->md.asyncIOCVar = PR_NewCondVar(thread->md.asyncIOLock);
00303        PR_ASSERT(thread->md.asyncIOCVar != NULL);
00304 
00305        if (thread->md.asyncIOLock == NULL || thread->md.asyncIOCVar == NULL)
00306               return PR_FAILURE;
00307        else
00308               return PR_SUCCESS;
00309 }
00310 
00311 PRStatus _MD_wait(PRThread *thread, PRIntervalTime timeout)
00312 {
00313 #pragma unused (timeout)
00314 
00315        _MD_SWITCH_CONTEXT(thread);
00316        return PR_SUCCESS;
00317 }
00318 
00319 
00320 void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout)
00321 {
00322     intn is;
00323     PRIntervalTime timein = PR_IntervalNow();
00324        PRStatus status = PR_SUCCESS;
00325 
00326     // Turn interrupts off to avoid a race over lock ownership with the callback
00327     // (which can fire at any time). Interrupts may stay off until we leave
00328     // this function, or another NSPR thread turns them back on. They certainly
00329     // stay off until PR_WaitCondVar() relinquishes the asyncIOLock lock, which
00330     // is what we care about.
00331        _PR_INTSOFF(is);
00332        PR_Lock(thread->md.asyncIOLock);
00333        if (timeout == PR_INTERVAL_NO_TIMEOUT) {
00334            while ((thread->io_pending) && (status == PR_SUCCESS))
00335                status = PR_WaitCondVar(thread->md.asyncIOCVar, PR_INTERVAL_NO_TIMEOUT);
00336        } else {
00337            while ((thread->io_pending) && ((PRIntervalTime)(PR_IntervalNow() - timein) < timeout) && (status == PR_SUCCESS))
00338                status = PR_WaitCondVar(thread->md.asyncIOCVar, timeout);
00339        }
00340        if ((status == PR_FAILURE) && (PR_GetError() == PR_PENDING_INTERRUPT_ERROR)) {
00341               thread->md.osErrCode = kEINTRErr;
00342        } else if (thread->io_pending) {
00343               thread->md.osErrCode = kETIMEDOUTErr;
00344               PR_SetError(PR_IO_TIMEOUT_ERROR, kETIMEDOUTErr);
00345        }
00346 
00347        thread->io_pending = PR_FALSE;
00348        PR_Unlock(thread->md.asyncIOLock);
00349        _PR_FAST_INTSON(is);
00350 }
00351 
00352 
00353 void DoneWaitingOnThisThread(PRThread *thread)
00354 {
00355     intn is;
00356 
00357     PR_ASSERT(thread->md.asyncIOLock->owner == NULL);
00358 
00359        // DoneWaitingOnThisThread() is called from OT notifiers and async file I/O
00360        // callbacks that can run at "interrupt" time (Classic Mac OS) or on pthreads
00361        // that may run concurrently with the main threads (Mac OS X). They can thus
00362        // be called when any NSPR thread is running, or even while NSPR is in a
00363        // thread context switch. It is therefore vital that we can guarantee to
00364        // be able to get the asyncIOLock without blocking (thus avoiding code
00365        // that makes assumptions about the current NSPR thread etc). To achieve
00366        // this, we use NSPR interrrupts as a semaphore on the lock; all code 
00367        // that grabs the lock also disables interrupts for the time the lock
00368        // is held. Callers of DoneWaitingOnThisThread() thus have to check whether
00369        // interrupts are already off, and, if so, simply set the missed_IO flag on
00370        // the CPU rather than calling this function.
00371        
00372        _PR_INTSOFF(is);
00373        PR_Lock(thread->md.asyncIOLock);
00374        thread->io_pending = PR_FALSE;
00375        /* let the waiting thread know that async IO completed */
00376        PR_NotifyCondVar(thread->md.asyncIOCVar);
00377        PR_Unlock(thread->md.asyncIOLock);
00378        _PR_FAST_INTSON(is);
00379 }
00380 
00381 
00382 PR_IMPLEMENT(void) PR_Mac_WaitForAsyncNotify(PRIntervalTime timeout)
00383 {
00384     intn is;
00385     PRIntervalTime timein = PR_IntervalNow();
00386        PRStatus status = PR_SUCCESS;
00387     PRThread *thread = _PR_MD_CURRENT_THREAD();
00388 
00389     // See commments in WaitOnThisThread()
00390        _PR_INTSOFF(is);
00391        PR_Lock(thread->md.asyncIOLock);
00392        if (timeout == PR_INTERVAL_NO_TIMEOUT) {
00393            while ((!thread->md.asyncNotifyPending) && (status == PR_SUCCESS))
00394                status = PR_WaitCondVar(thread->md.asyncIOCVar, PR_INTERVAL_NO_TIMEOUT);
00395        } else {
00396            while ((!thread->md.asyncNotifyPending) && ((PRIntervalTime)(PR_IntervalNow() - timein) < timeout) && (status == PR_SUCCESS))
00397                status = PR_WaitCondVar(thread->md.asyncIOCVar, timeout);
00398        }
00399        if ((status == PR_FAILURE) && (PR_GetError() == PR_PENDING_INTERRUPT_ERROR)) {
00400               thread->md.osErrCode = kEINTRErr;
00401        } else if (!thread->md.asyncNotifyPending) {
00402               thread->md.osErrCode = kETIMEDOUTErr;
00403               PR_SetError(PR_IO_TIMEOUT_ERROR, kETIMEDOUTErr);
00404        }
00405        thread->md.asyncNotifyPending = PR_FALSE;
00406        PR_Unlock(thread->md.asyncIOLock);
00407        _PR_FAST_INTSON(is);
00408 }
00409 
00410 
00411 void AsyncNotify(PRThread *thread)
00412 {
00413     intn is;
00414        
00415     PR_ASSERT(thread->md.asyncIOLock->owner == NULL);
00416 
00417     // See commments in DoneWaitingOnThisThread()
00418        _PR_INTSOFF(is);
00419        PR_Lock(thread->md.asyncIOLock);
00420        thread->md.asyncNotifyPending = PR_TRUE;
00421        /* let the waiting thread know that async IO completed */
00422        PR_NotifyCondVar(thread->md.asyncIOCVar);
00423        PR_Unlock(thread->md.asyncIOLock);
00424        _PR_FAST_INTSON(is);
00425 }
00426 
00427 
00428 PR_IMPLEMENT(void) PR_Mac_PostAsyncNotify(PRThread *thread)
00429 {
00430        _PRCPU *  cpu = _PR_MD_CURRENT_CPU();
00431        
00432        if (_PR_MD_GET_INTSOFF()) {
00433               thread->md.missedAsyncNotify = PR_TRUE;
00434               cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
00435        } else {
00436               AsyncNotify(thread);
00437        }
00438 }
00439 
00440 
00441 //##############################################################################
00442 //##############################################################################
00443 #pragma mark -
00444 #pragma mark PROCESS SUPPORT FUNCTIONS
00445 
00446 PRProcess * _MD_CreateProcess(
00447     const char *path,
00448     char *const *argv,
00449     char *const *envp,
00450     const PRProcessAttr *attr)
00451 {
00452 #pragma unused (path, argv, envp, attr)
00453 
00454        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
00455        return NULL;
00456 }
00457 
00458 PRStatus _MD_DetachProcess(PRProcess *process)
00459 {
00460 #pragma unused (process)
00461 
00462        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
00463        return PR_FAILURE;
00464 }
00465 
00466 PRStatus _MD_WaitProcess(PRProcess *process, PRInt32 *exitCode)
00467 {
00468 #pragma unused (process, exitCode)
00469 
00470        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
00471        return PR_FAILURE;
00472 }
00473 
00474 PRStatus _MD_KillProcess(PRProcess *process)
00475 {
00476 #pragma unused (process)
00477 
00478        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
00479        return PR_FAILURE;
00480 }
00481 
00482 //##############################################################################
00483 //##############################################################################
00484 #pragma mark -
00485 #pragma mark ATOMIC OPERATIONS
00486 
00487 #ifdef _PR_HAVE_ATOMIC_OPS
00488 PRInt32
00489 _MD_AtomicSet(PRInt32 *val, PRInt32 newval)
00490 {
00491     PRInt32 rv;
00492     do  {
00493         rv = *val;
00494     } while (!OTCompareAndSwap32(rv, newval, (UInt32*)val));
00495 
00496     return rv;
00497 }
00498 
00499 #endif // _PR_HAVE_ATOMIC_OPS
00500 
00501 //##############################################################################
00502 //##############################################################################
00503 #pragma mark -
00504 #pragma mark INTERRUPT SUPPORT
00505 
00506 #if TARGET_CARBON
00507 
00508 /*
00509      This critical region support is required for Mac NSPR to work correctly on dual CPU
00510      machines on Mac OS X. This note explains why.
00511 
00512      NSPR uses a timer task, and has callbacks for async file I/O and Open Transport
00513      whose runtime behaviour differs depending on environment. On "Classic" Mac OS
00514      these run at "interrupt" time (OS-level interrupts, that is, not NSPR interrupts),
00515      and can thus preempt other code, but they always run to completion.
00516 
00517      On Mac OS X, these are all emulated using MP tasks, which sit atop pthreads. Thus,
00518      they can be preempted at any time (and not necessarily run to completion), and can
00519      also run *concurrently* with eachother, and with application code, on multiple
00520      CPU machines. Note that all NSPR threads are emulated, and all run on the main
00521      application MP task.
00522 
00523      We thus have to use MP critical sections to protect data that is shared between
00524      the various callbacks and the main MP thread. It so happens that NSPR has this
00525      concept of software interrupts, and making interrupt-off times be critical
00526      sections works.
00527 
00528 */
00529 
00530 
00531 /*  
00532     Whether to use critical regions. True if running on Mac OS X and later
00533 */
00534 
00535 PRBool  gUseCriticalRegions;
00536 
00537 /*
00538     Count of the number of times we've entered the critical region.
00539     We need this because ENTER_CRITICAL_REGION() will *not* block when
00540     called from different NSPR threads (which all run on one MP thread),
00541     and we need to ensure that when code turns interrupts back on (by
00542     settings _pr_intsOff to 0) we exit the critical section enough times
00543     to leave it.
00544 */
00545 
00546 PRInt32 gCriticalRegionEntryCount;
00547 
00548 
00549 void _MD_SetIntsOff(PRInt32 ints)
00550 {
00551     ENTER_CRITICAL_REGION();
00552     gCriticalRegionEntryCount ++;
00553     
00554     _pr_intsOff = ints;
00555     
00556     if (!ints)
00557     {
00558         PRInt32     i = gCriticalRegionEntryCount;
00559 
00560         gCriticalRegionEntryCount = 0;
00561         for ( ;i > 0; i --) {
00562             LEAVE_CRITICAL_REGION();
00563         }
00564     }
00565 }
00566 
00567 
00568 #endif /* TARGET_CARBON */
00569 
00570 
00571 //##############################################################################
00572 //##############################################################################
00573 #pragma mark -
00574 #pragma mark CRITICAL REGION SUPPORT
00575 
00576 
00577 static PRBool RunningOnOSX()
00578 {
00579     long    systemVersion;
00580     OSErr   err = Gestalt(gestaltSystemVersion, &systemVersion);
00581     return (err == noErr) && (systemVersion >= 0x00001000);
00582 }
00583 
00584 
00585 #if MAC_CRITICAL_REGIONS
00586 
00587 MDCriticalRegionID  gCriticalRegion;
00588 
00589 void InitCriticalRegion()
00590 {
00591     OSStatus    err;    
00592     
00593     // we only need to do critical region stuff on Mac OS X    
00594     gUseCriticalRegions = RunningOnOSX();
00595     if (!gUseCriticalRegions) return;
00596     
00597     err = MD_CriticalRegionCreate(&gCriticalRegion);
00598     PR_ASSERT(err == noErr);
00599 }
00600 
00601 void TermCriticalRegion()
00602 {
00603     OSStatus    err;    
00604 
00605     if (!gUseCriticalRegions) return;
00606 
00607     err = MD_CriticalRegionDelete(gCriticalRegion);
00608     PR_ASSERT(err == noErr);
00609 }
00610 
00611 
00612 void EnterCritialRegion()
00613 {
00614     OSStatus    err;
00615     
00616     if (!gUseCriticalRegions) return;
00617 
00618     PR_ASSERT(gCriticalRegion != kInvalidID);
00619     
00620     /* Change to a non-infinite timeout for debugging purposes */
00621     err = MD_CriticalRegionEnter(gCriticalRegion, kDurationForever /* 10000 * kDurationMillisecond */ );
00622     PR_ASSERT(err == noErr);
00623 }
00624 
00625 void LeaveCritialRegion()
00626 {
00627     OSStatus    err;    
00628 
00629     if (!gUseCriticalRegions) return;
00630 
00631     PR_ASSERT(gCriticalRegion != kInvalidID);
00632 
00633     err = MD_CriticalRegionExit(gCriticalRegion);
00634     PR_ASSERT(err == noErr);
00635 }
00636 
00637 
00638 #endif // MAC_CRITICAL_REGIONS
00639 
00640 //##############################################################################
00641 //##############################################################################
00642 #pragma mark -
00643 #pragma mark IDLE SEMAPHORE SUPPORT
00644 
00645 /*
00646      Since the WaitNextEvent() in _MD_PauseCPU() is causing all sorts of
00647      headache under Mac OS X we're going to switch to MPWaitOnSemaphore()
00648      which should do what we want
00649 */
00650 
00651 #if TARGET_CARBON
00652 PRBool                             gUseIdleSemaphore = PR_FALSE;
00653 MPSemaphoreID               gIdleSemaphore = NULL;
00654 #endif
00655 
00656 void InitIdleSemaphore()
00657 {
00658     // we only need to do idle semaphore stuff on Mac OS X
00659 #if TARGET_CARBON
00660        gUseIdleSemaphore = RunningOnOSX();
00661        if (gUseIdleSemaphore)
00662        {
00663               OSStatus  err = MPCreateSemaphore(1 /* max value */, 0 /* initial value */, &gIdleSemaphore);
00664               PR_ASSERT(err == noErr);
00665        }
00666 #endif
00667 }
00668 
00669 void TermIdleSemaphore()
00670 {
00671 #if TARGET_CARBON
00672        if (gUseIdleSemaphore)
00673        {
00674               OSStatus  err = MPDeleteSemaphore(gIdleSemaphore);
00675               PR_ASSERT(err == noErr);
00676               gUseIdleSemaphore = NULL;
00677        }
00678 #endif
00679 }
00680 
00681 
00682 void WaitOnIdleSemaphore(PRIntervalTime timeout)
00683 {
00684 #if TARGET_CARBON
00685        if (gUseIdleSemaphore)
00686        {
00687               OSStatus  err = MPWaitOnSemaphore(gIdleSemaphore, kDurationMillisecond * PR_IntervalToMilliseconds(timeout));
00688               PR_ASSERT(err == noErr);
00689        }
00690        else
00691 #endif
00692        {
00693               EventRecord   theEvent;
00694               /*
00695               ** Calling WaitNextEvent() here is suboptimal. This routine should
00696               ** pause the process until IO or the timeout occur, yielding time to
00697               ** other processes on operating systems that require this (Mac OS classic).
00698               ** WaitNextEvent() may incur too much latency, and has other problems,
00699               ** such as the potential to drop suspend/resume events.
00700               */
00701               (void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
00702        }
00703 }
00704 
00705 
00706 void SignalIdleSemaphore()
00707 {
00708 #if TARGET_CARBON
00709        if (gUseIdleSemaphore)
00710        {
00711               // often we won't be waiting on the semaphore here, so ignore any errors
00712               (void)MPSignalSemaphore(gIdleSemaphore);
00713        }
00714        else
00715 #endif
00716        {
00717               WakeUpProcess(&gApplicationProcess);
00718        }
00719 }
00720 
00721