~ubuntu-branches/ubuntu/quantal/nspr/quantal-security

« back to all changes in this revision

Viewing changes to mozilla/nsprpub/pr/src/md/mac/macthr.c

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Sack
  • Date: 2009-08-10 11:34:26 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20090810113426-3uv4diflrkcbdimm
Tags: 4.8-0ubuntu1
* New upstream release: 4.8 (LP: #387812)
* adjust patches to changed upstreanm codebase
  - update debian/patches/99_configure.patch
* update shlibs symbols to include new API elements
  - update debian/libnspr4-0d.symbols

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
4
 
 *
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/
9
 
 *
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
13
 
 * License.
14
 
 *
15
 
 * The Original Code is the Netscape Portable Runtime (NSPR).
16
 
 *
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.
21
 
 *
22
 
 * Contributor(s):
23
 
 *
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.
35
 
 *
36
 
 * ***** END LICENSE BLOCK ***** */
37
 
 
38
 
#include "primpl.h"
39
 
 
40
 
#include <string.h>
41
 
 
42
 
#include <MacTypes.h>
43
 
#include <Timer.h>
44
 
#include <OSUtils.h>
45
 
#include <Math64.h>
46
 
#include <LowMem.h>
47
 
#include <Multiprocessing.h>
48
 
#include <Gestalt.h>
49
 
 
50
 
#include "mdcriticalregion.h"
51
 
 
52
 
TimerUPP        gTimerCallbackUPP       = NULL;
53
 
PRThread *      gPrimaryThread          = NULL;
54
 
 
55
 
ProcessSerialNumber             gApplicationProcess;
56
 
 
57
 
PR_IMPLEMENT(PRThread *) PR_GetPrimaryThread()
58
 
{
59
 
        return gPrimaryThread;
60
 
}
61
 
 
62
 
//##############################################################################
63
 
//##############################################################################
64
 
#pragma mark -
65
 
#pragma mark CREATING MACINTOSH THREAD STACKS
66
 
 
67
 
#if defined(GC_LEAK_DETECTOR)
68
 
extern void* GC_malloc_atomic(PRUint32 size);
69
 
#endif
70
 
 
71
 
/*
72
 
**      Allocate a new memory segment.  We allocate it from our figment heap.  Currently,
73
 
**      it is being used for per thread stack space.
74
 
**      
75
 
**      Return the segment's access rights and size.  vaddr is used on Unix platforms to
76
 
**      map an existing address for the segment.
77
 
*/
78
 
PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
79
 
{
80
 
        PR_ASSERT(seg != 0);
81
 
        PR_ASSERT(size != 0);
82
 
        PR_ASSERT(vaddr == 0);
83
 
 
84
 
        /*      
85
 
        ** Take the actual memory for the segment out of our Figment heap.
86
 
        */
87
 
 
88
 
#if defined(GC_LEAK_DETECTOR)
89
 
        seg->vaddr = (char *)GC_malloc_atomic(size);
90
 
#else
91
 
        seg->vaddr = (char *)malloc(size);
92
 
#endif
93
 
 
94
 
        if (seg->vaddr == NULL) {
95
 
 
96
 
#if DEBUG
97
 
                DebugStr("\p_MD_AllocSegment failed.");
98
 
#endif
99
 
 
100
 
                return PR_FAILURE;
101
 
        }
102
 
 
103
 
        seg->size = size;       
104
 
 
105
 
        return PR_SUCCESS;
106
 
}
107
 
 
108
 
 
109
 
/*
110
 
**      Free previously allocated memory segment.
111
 
*/
112
 
void _MD_FreeSegment(PRSegment *seg)
113
 
{
114
 
        PR_ASSERT((seg->flags & _PR_SEG_VM) == 0);
115
 
 
116
 
        if (seg->vaddr != NULL)
117
 
                free(seg->vaddr);
118
 
}
119
 
 
120
 
 
121
 
/*
122
 
**      The thread's stack has been allocated and its fields are already properly filled
123
 
**      in by PR.  Perform any debugging related initialization here.
124
 
**
125
 
**      Put a recognizable pattern so that we can find it from Macsbug.
126
 
**      Put a cookie at the top of the stack so that we can find it from Macsbug.
127
 
*/
128
 
void _MD_InitStack(PRThreadStack *ts, int redZoneBytes)
129
 
        {
130
 
#pragma unused (redZoneBytes)
131
 
#if DEVELOPER_DEBUG
132
 
        //      Put a cookie at the top of the stack so that we can find 
133
 
        //      it from Macsbug.
134
 
        
135
 
        memset(ts->allocBase, 0xDC, ts->stackSize);
136
 
        
137
 
        ((UInt32 *)ts->stackTop)[-1] = 0xBEEFCAFE;
138
 
        ((UInt32 *)ts->stackTop)[-2] = (UInt32)gPrimaryThread;
139
 
        ((UInt32 *)ts->stackTop)[-3] = (UInt32)(ts);
140
 
        ((UInt32 *)ts->stackBottom)[0] = 0xCAFEBEEF;
141
 
#else
142
 
#pragma unused (ts)
143
 
#endif  
144
 
        }
145
 
 
146
 
extern void _MD_ClearStack(PRThreadStack *ts)
147
 
        {
148
 
#if DEVELOPER_DEBUG
149
 
        //      Clear out our cookies. 
150
 
        
151
 
        memset(ts->allocBase, 0xEF, ts->allocSize);
152
 
        ((UInt32 *)ts->stackTop)[-1] = 0;
153
 
        ((UInt32 *)ts->stackTop)[-2] = 0;
154
 
        ((UInt32 *)ts->stackTop)[-3] = 0;
155
 
        ((UInt32 *)ts->stackBottom)[0] = 0;
156
 
#else
157
 
#pragma unused (ts)
158
 
#endif
159
 
        }
160
 
 
161
 
 
162
 
//##############################################################################
163
 
//##############################################################################
164
 
#pragma mark -
165
 
#pragma mark TIME MANAGER-BASED CLOCK
166
 
 
167
 
// On Mac OS X, it's possible for the application to spend lots of time
168
 
// in WaitNextEvent, yielding to other applications. Since NSPR threads are
169
 
// cooperative here, this means that NSPR threads will also get very little
170
 
// time to run. To kick ourselves out of a WaitNextEvent call when we have
171
 
// determined that it's time to schedule another thread, the Timer Task
172
 
// (which fires every 8ms, even when other apps have the CPU) calls WakeUpProcess.
173
 
// We only want to do this on Mac OS X; the gTimeManagerTaskDoesWUP variable
174
 
// indicates when we're running on that OS.
175
 
//
176
 
// Note that the TimerCallback makes use of gApplicationProcess. We need to
177
 
// have set this up before the first possible run of the timer task; we do
178
 
// so in _MD_EarlyInit().
179
 
static Boolean  gTimeManagerTaskDoesWUP;
180
 
 
181
 
static TMTask   gTimeManagerTaskElem;
182
 
 
183
 
extern void _MD_IOInterrupt(void);
184
 
_PRInterruptTable _pr_interruptTable[] = {
185
 
    { "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, },
186
 
    { "i/o", _PR_MISSED_IO, _MD_IOInterrupt, },
187
 
    { 0 }
188
 
};
189
 
 
190
 
#define kMacTimerInMiliSecs 8L
191
 
 
192
 
pascal void TimerCallback(TMTaskPtr tmTaskPtr)
193
 
{
194
 
    _PRCPU *cpu = _PR_MD_CURRENT_CPU();
195
 
    PRIntn is;
196
 
 
197
 
    if (_PR_MD_GET_INTSOFF()) {
198
 
        cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
199
 
        PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
200
 
        return;
201
 
    }
202
 
 
203
 
    _PR_INTSOFF(is);
204
 
 
205
 
    //  And tell nspr that a clock interrupt occured.
206
 
    _PR_ClockInterrupt();
207
 
        
208
 
    if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority))) {
209
 
        if (gTimeManagerTaskDoesWUP) {
210
 
            // We only want to call WakeUpProcess if we know that NSPR has managed to switch threads
211
 
            // since the last call, otherwise we end up spewing out WakeUpProcess() calls while the
212
 
            // application is blocking somewhere. This can interfere with events loops other than
213
 
            // our own (see bug 158927).
214
 
            if (UnsignedWideToUInt64(cpu->md.lastThreadSwitch) > UnsignedWideToUInt64(cpu->md.lastWakeUpProcess))
215
 
            {
216
 
                WakeUpProcess(&gApplicationProcess);
217
 
                cpu->md.lastWakeUpProcess = UpTime();
218
 
            }
219
 
        }
220
 
        _PR_SET_RESCHED_FLAG();
221
 
        }
222
 
        
223
 
    _PR_FAST_INTSON(is);
224
 
 
225
 
    //  Reset the clock timer so that we fire again.
226
 
    PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
227
 
}
228
 
 
229
 
 
230
 
void _MD_StartInterrupts(void)
231
 
{
232
 
        gPrimaryThread = _PR_MD_CURRENT_THREAD();
233
 
 
234
 
        gTimeManagerTaskDoesWUP = RunningOnOSX();
235
 
 
236
 
        if ( !gTimerCallbackUPP )
237
 
                gTimerCallbackUPP = NewTimerUPP(TimerCallback);
238
 
 
239
 
        //      Fill in the Time Manager queue element
240
 
        
241
 
        gTimeManagerTaskElem.tmAddr = (TimerUPP)gTimerCallbackUPP;
242
 
        gTimeManagerTaskElem.tmCount = 0;
243
 
        gTimeManagerTaskElem.tmWakeUp = 0;
244
 
        gTimeManagerTaskElem.tmReserved = 0;
245
 
 
246
 
        //      Make sure that our time manager task is ready to go.
247
 
        InsTime((QElemPtr)&gTimeManagerTaskElem);
248
 
        
249
 
        PrimeTime((QElemPtr)&gTimeManagerTaskElem, kMacTimerInMiliSecs);
250
 
}
251
 
 
252
 
void _MD_StopInterrupts(void)
253
 
{
254
 
        if (gTimeManagerTaskElem.tmAddr != NULL) {
255
 
                RmvTime((QElemPtr)&gTimeManagerTaskElem);
256
 
                gTimeManagerTaskElem.tmAddr = NULL;
257
 
        }
258
 
}
259
 
 
260
 
 
261
 
#define MAX_PAUSE_TIMEOUT_MS    500
262
 
 
263
 
void _MD_PauseCPU(PRIntervalTime timeout)
264
 
{
265
 
    if (timeout != PR_INTERVAL_NO_WAIT)
266
 
    {
267
 
        // There is a race condition entering the critical section
268
 
        // in AsyncIOCompletion (and probably elsewhere) that can
269
 
        // causes deadlock for the duration of this timeout. To
270
 
        // work around this, use a max 500ms timeout for now.
271
 
        // See bug 99561 for details.
272
 
        if (PR_IntervalToMilliseconds(timeout) > MAX_PAUSE_TIMEOUT_MS)
273
 
            timeout = PR_MillisecondsToInterval(MAX_PAUSE_TIMEOUT_MS);
274
 
 
275
 
        WaitOnIdleSemaphore(timeout);
276
 
        (void) _MD_IOInterrupt();
277
 
    }
278
 
}
279
 
 
280
 
void _MD_InitRunningCPU(_PRCPU* cpu)
281
 
{
282
 
    cpu->md.trackScheduling = RunningOnOSX();
283
 
    if (cpu->md.trackScheduling) {
284
 
        AbsoluteTime    zeroTime = {0, 0};
285
 
        cpu->md.lastThreadSwitch = UpTime();
286
 
        cpu->md.lastWakeUpProcess = zeroTime;
287
 
    }
288
 
}
289
 
 
290
 
 
291
 
//##############################################################################
292
 
//##############################################################################
293
 
#pragma mark -
294
 
#pragma mark THREAD SUPPORT FUNCTIONS
295
 
 
296
 
#include <OpenTransport.h> /* for error codes */
297
 
 
298
 
PRStatus _MD_InitThread(PRThread *thread)
299
 
{
300
 
        thread->md.asyncIOLock = PR_NewLock();
301
 
        PR_ASSERT(thread->md.asyncIOLock != NULL);
302
 
        thread->md.asyncIOCVar = PR_NewCondVar(thread->md.asyncIOLock);
303
 
        PR_ASSERT(thread->md.asyncIOCVar != NULL);
304
 
 
305
 
        if (thread->md.asyncIOLock == NULL || thread->md.asyncIOCVar == NULL)
306
 
                return PR_FAILURE;
307
 
        else
308
 
                return PR_SUCCESS;
309
 
}
310
 
 
311
 
PRStatus _MD_wait(PRThread *thread, PRIntervalTime timeout)
312
 
{
313
 
#pragma unused (timeout)
314
 
 
315
 
        _MD_SWITCH_CONTEXT(thread);
316
 
        return PR_SUCCESS;
317
 
}
318
 
 
319
 
 
320
 
void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout)
321
 
{
322
 
    intn is;
323
 
    PRIntervalTime timein = PR_IntervalNow();
324
 
        PRStatus status = PR_SUCCESS;
325
 
 
326
 
    // Turn interrupts off to avoid a race over lock ownership with the callback
327
 
    // (which can fire at any time). Interrupts may stay off until we leave
328
 
    // this function, or another NSPR thread turns them back on. They certainly
329
 
    // stay off until PR_WaitCondVar() relinquishes the asyncIOLock lock, which
330
 
    // is what we care about.
331
 
        _PR_INTSOFF(is);
332
 
        PR_Lock(thread->md.asyncIOLock);
333
 
        if (timeout == PR_INTERVAL_NO_TIMEOUT) {
334
 
            while ((thread->io_pending) && (status == PR_SUCCESS))
335
 
                status = PR_WaitCondVar(thread->md.asyncIOCVar, PR_INTERVAL_NO_TIMEOUT);
336
 
        } else {
337
 
            while ((thread->io_pending) && ((PRIntervalTime)(PR_IntervalNow() - timein) < timeout) && (status == PR_SUCCESS))
338
 
                status = PR_WaitCondVar(thread->md.asyncIOCVar, timeout);
339
 
        }
340
 
        if ((status == PR_FAILURE) && (PR_GetError() == PR_PENDING_INTERRUPT_ERROR)) {
341
 
                thread->md.osErrCode = kEINTRErr;
342
 
        } else if (thread->io_pending) {
343
 
                thread->md.osErrCode = kETIMEDOUTErr;
344
 
                PR_SetError(PR_IO_TIMEOUT_ERROR, kETIMEDOUTErr);
345
 
        }
346
 
 
347
 
        thread->io_pending = PR_FALSE;
348
 
        PR_Unlock(thread->md.asyncIOLock);
349
 
        _PR_FAST_INTSON(is);
350
 
}
351
 
 
352
 
 
353
 
void DoneWaitingOnThisThread(PRThread *thread)
354
 
{
355
 
    intn is;
356
 
 
357
 
    PR_ASSERT(thread->md.asyncIOLock->owner == NULL);
358
 
 
359
 
        // DoneWaitingOnThisThread() is called from OT notifiers and async file I/O
360
 
        // callbacks that can run at "interrupt" time (Classic Mac OS) or on pthreads
361
 
        // that may run concurrently with the main threads (Mac OS X). They can thus
362
 
        // be called when any NSPR thread is running, or even while NSPR is in a
363
 
        // thread context switch. It is therefore vital that we can guarantee to
364
 
        // be able to get the asyncIOLock without blocking (thus avoiding code
365
 
        // that makes assumptions about the current NSPR thread etc). To achieve
366
 
        // this, we use NSPR interrrupts as a semaphore on the lock; all code 
367
 
        // that grabs the lock also disables interrupts for the time the lock
368
 
        // is held. Callers of DoneWaitingOnThisThread() thus have to check whether
369
 
        // interrupts are already off, and, if so, simply set the missed_IO flag on
370
 
        // the CPU rather than calling this function.
371
 
        
372
 
        _PR_INTSOFF(is);
373
 
        PR_Lock(thread->md.asyncIOLock);
374
 
        thread->io_pending = PR_FALSE;
375
 
        /* let the waiting thread know that async IO completed */
376
 
        PR_NotifyCondVar(thread->md.asyncIOCVar);
377
 
        PR_Unlock(thread->md.asyncIOLock);
378
 
        _PR_FAST_INTSON(is);
379
 
}
380
 
 
381
 
 
382
 
PR_IMPLEMENT(void) PR_Mac_WaitForAsyncNotify(PRIntervalTime timeout)
383
 
{
384
 
    intn is;
385
 
    PRIntervalTime timein = PR_IntervalNow();
386
 
        PRStatus status = PR_SUCCESS;
387
 
    PRThread *thread = _PR_MD_CURRENT_THREAD();
388
 
 
389
 
    // See commments in WaitOnThisThread()
390
 
        _PR_INTSOFF(is);
391
 
        PR_Lock(thread->md.asyncIOLock);
392
 
        if (timeout == PR_INTERVAL_NO_TIMEOUT) {
393
 
            while ((!thread->md.asyncNotifyPending) && (status == PR_SUCCESS))
394
 
                status = PR_WaitCondVar(thread->md.asyncIOCVar, PR_INTERVAL_NO_TIMEOUT);
395
 
        } else {
396
 
            while ((!thread->md.asyncNotifyPending) && ((PRIntervalTime)(PR_IntervalNow() - timein) < timeout) && (status == PR_SUCCESS))
397
 
                status = PR_WaitCondVar(thread->md.asyncIOCVar, timeout);
398
 
        }
399
 
        if ((status == PR_FAILURE) && (PR_GetError() == PR_PENDING_INTERRUPT_ERROR)) {
400
 
                thread->md.osErrCode = kEINTRErr;
401
 
        } else if (!thread->md.asyncNotifyPending) {
402
 
                thread->md.osErrCode = kETIMEDOUTErr;
403
 
                PR_SetError(PR_IO_TIMEOUT_ERROR, kETIMEDOUTErr);
404
 
        }
405
 
        thread->md.asyncNotifyPending = PR_FALSE;
406
 
        PR_Unlock(thread->md.asyncIOLock);
407
 
        _PR_FAST_INTSON(is);
408
 
}
409
 
 
410
 
 
411
 
void AsyncNotify(PRThread *thread)
412
 
{
413
 
    intn is;
414
 
        
415
 
    PR_ASSERT(thread->md.asyncIOLock->owner == NULL);
416
 
 
417
 
    // See commments in DoneWaitingOnThisThread()
418
 
        _PR_INTSOFF(is);
419
 
        PR_Lock(thread->md.asyncIOLock);
420
 
        thread->md.asyncNotifyPending = PR_TRUE;
421
 
        /* let the waiting thread know that async IO completed */
422
 
        PR_NotifyCondVar(thread->md.asyncIOCVar);
423
 
        PR_Unlock(thread->md.asyncIOLock);
424
 
        _PR_FAST_INTSON(is);
425
 
}
426
 
 
427
 
 
428
 
PR_IMPLEMENT(void) PR_Mac_PostAsyncNotify(PRThread *thread)
429
 
{
430
 
        _PRCPU *  cpu = _PR_MD_CURRENT_CPU();
431
 
        
432
 
        if (_PR_MD_GET_INTSOFF()) {
433
 
                thread->md.missedAsyncNotify = PR_TRUE;
434
 
                cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
435
 
        } else {
436
 
                AsyncNotify(thread);
437
 
        }
438
 
}
439
 
 
440
 
 
441
 
//##############################################################################
442
 
//##############################################################################
443
 
#pragma mark -
444
 
#pragma mark PROCESS SUPPORT FUNCTIONS
445
 
 
446
 
PRProcess * _MD_CreateProcess(
447
 
    const char *path,
448
 
    char *const *argv,
449
 
    char *const *envp,
450
 
    const PRProcessAttr *attr)
451
 
{
452
 
#pragma unused (path, argv, envp, attr)
453
 
 
454
 
        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
455
 
        return NULL;
456
 
}
457
 
 
458
 
PRStatus _MD_DetachProcess(PRProcess *process)
459
 
{
460
 
#pragma unused (process)
461
 
 
462
 
        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
463
 
        return PR_FAILURE;
464
 
}
465
 
 
466
 
PRStatus _MD_WaitProcess(PRProcess *process, PRInt32 *exitCode)
467
 
{
468
 
#pragma unused (process, exitCode)
469
 
 
470
 
        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
471
 
        return PR_FAILURE;
472
 
}
473
 
 
474
 
PRStatus _MD_KillProcess(PRProcess *process)
475
 
{
476
 
#pragma unused (process)
477
 
 
478
 
        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
479
 
        return PR_FAILURE;
480
 
}
481
 
 
482
 
//##############################################################################
483
 
//##############################################################################
484
 
#pragma mark -
485
 
#pragma mark ATOMIC OPERATIONS
486
 
 
487
 
#ifdef _PR_HAVE_ATOMIC_OPS
488
 
PRInt32
489
 
_MD_AtomicSet(PRInt32 *val, PRInt32 newval)
490
 
{
491
 
    PRInt32 rv;
492
 
    do  {
493
 
        rv = *val;
494
 
    } while (!OTCompareAndSwap32(rv, newval, (UInt32*)val));
495
 
 
496
 
    return rv;
497
 
}
498
 
 
499
 
#endif // _PR_HAVE_ATOMIC_OPS
500
 
 
501
 
//##############################################################################
502
 
//##############################################################################
503
 
#pragma mark -
504
 
#pragma mark INTERRUPT SUPPORT
505
 
 
506
 
#if TARGET_CARBON
507
 
 
508
 
/*
509
 
     This critical region support is required for Mac NSPR to work correctly on dual CPU
510
 
     machines on Mac OS X. This note explains why.
511
 
 
512
 
     NSPR uses a timer task, and has callbacks for async file I/O and Open Transport
513
 
     whose runtime behaviour differs depending on environment. On "Classic" Mac OS
514
 
     these run at "interrupt" time (OS-level interrupts, that is, not NSPR interrupts),
515
 
     and can thus preempt other code, but they always run to completion.
516
 
 
517
 
     On Mac OS X, these are all emulated using MP tasks, which sit atop pthreads. Thus,
518
 
     they can be preempted at any time (and not necessarily run to completion), and can
519
 
     also run *concurrently* with eachother, and with application code, on multiple
520
 
     CPU machines. Note that all NSPR threads are emulated, and all run on the main
521
 
     application MP task.
522
 
 
523
 
     We thus have to use MP critical sections to protect data that is shared between
524
 
     the various callbacks and the main MP thread. It so happens that NSPR has this
525
 
     concept of software interrupts, and making interrupt-off times be critical
526
 
     sections works.
527
 
 
528
 
*/
529
 
 
530
 
 
531
 
/*  
532
 
    Whether to use critical regions. True if running on Mac OS X and later
533
 
*/
534
 
 
535
 
PRBool  gUseCriticalRegions;
536
 
 
537
 
/*
538
 
    Count of the number of times we've entered the critical region.
539
 
    We need this because ENTER_CRITICAL_REGION() will *not* block when
540
 
    called from different NSPR threads (which all run on one MP thread),
541
 
    and we need to ensure that when code turns interrupts back on (by
542
 
    settings _pr_intsOff to 0) we exit the critical section enough times
543
 
    to leave it.
544
 
*/
545
 
 
546
 
PRInt32 gCriticalRegionEntryCount;
547
 
 
548
 
 
549
 
void _MD_SetIntsOff(PRInt32 ints)
550
 
{
551
 
    ENTER_CRITICAL_REGION();
552
 
    gCriticalRegionEntryCount ++;
553
 
    
554
 
    _pr_intsOff = ints;
555
 
    
556
 
    if (!ints)
557
 
    {
558
 
        PRInt32     i = gCriticalRegionEntryCount;
559
 
 
560
 
        gCriticalRegionEntryCount = 0;
561
 
        for ( ;i > 0; i --) {
562
 
            LEAVE_CRITICAL_REGION();
563
 
        }
564
 
    }
565
 
}
566
 
 
567
 
 
568
 
#endif /* TARGET_CARBON */
569
 
 
570
 
 
571
 
//##############################################################################
572
 
//##############################################################################
573
 
#pragma mark -
574
 
#pragma mark CRITICAL REGION SUPPORT
575
 
 
576
 
 
577
 
static PRBool RunningOnOSX()
578
 
{
579
 
    long    systemVersion;
580
 
    OSErr   err = Gestalt(gestaltSystemVersion, &systemVersion);
581
 
    return (err == noErr) && (systemVersion >= 0x00001000);
582
 
}
583
 
 
584
 
 
585
 
#if MAC_CRITICAL_REGIONS
586
 
 
587
 
MDCriticalRegionID  gCriticalRegion;
588
 
 
589
 
void InitCriticalRegion()
590
 
{
591
 
    OSStatus    err;    
592
 
    
593
 
    // we only need to do critical region stuff on Mac OS X    
594
 
    gUseCriticalRegions = RunningOnOSX();
595
 
    if (!gUseCriticalRegions) return;
596
 
    
597
 
    err = MD_CriticalRegionCreate(&gCriticalRegion);
598
 
    PR_ASSERT(err == noErr);
599
 
}
600
 
 
601
 
void TermCriticalRegion()
602
 
{
603
 
    OSStatus    err;    
604
 
 
605
 
    if (!gUseCriticalRegions) return;
606
 
 
607
 
    err = MD_CriticalRegionDelete(gCriticalRegion);
608
 
    PR_ASSERT(err == noErr);
609
 
}
610
 
 
611
 
 
612
 
void EnterCritialRegion()
613
 
{
614
 
    OSStatus    err;
615
 
    
616
 
    if (!gUseCriticalRegions) return;
617
 
 
618
 
    PR_ASSERT(gCriticalRegion != kInvalidID);
619
 
    
620
 
    /* Change to a non-infinite timeout for debugging purposes */
621
 
    err = MD_CriticalRegionEnter(gCriticalRegion, kDurationForever /* 10000 * kDurationMillisecond */ );
622
 
    PR_ASSERT(err == noErr);
623
 
}
624
 
 
625
 
void LeaveCritialRegion()
626
 
{
627
 
    OSStatus    err;    
628
 
 
629
 
    if (!gUseCriticalRegions) return;
630
 
 
631
 
    PR_ASSERT(gCriticalRegion != kInvalidID);
632
 
 
633
 
    err = MD_CriticalRegionExit(gCriticalRegion);
634
 
    PR_ASSERT(err == noErr);
635
 
}
636
 
 
637
 
 
638
 
#endif // MAC_CRITICAL_REGIONS
639
 
 
640
 
//##############################################################################
641
 
//##############################################################################
642
 
#pragma mark -
643
 
#pragma mark IDLE SEMAPHORE SUPPORT
644
 
 
645
 
/*
646
 
     Since the WaitNextEvent() in _MD_PauseCPU() is causing all sorts of
647
 
     headache under Mac OS X we're going to switch to MPWaitOnSemaphore()
648
 
     which should do what we want
649
 
*/
650
 
 
651
 
#if TARGET_CARBON
652
 
PRBool                                  gUseIdleSemaphore = PR_FALSE;
653
 
MPSemaphoreID                   gIdleSemaphore = NULL;
654
 
#endif
655
 
 
656
 
void InitIdleSemaphore()
657
 
{
658
 
    // we only need to do idle semaphore stuff on Mac OS X
659
 
#if TARGET_CARBON
660
 
        gUseIdleSemaphore = RunningOnOSX();
661
 
        if (gUseIdleSemaphore)
662
 
        {
663
 
                OSStatus  err = MPCreateSemaphore(1 /* max value */, 0 /* initial value */, &gIdleSemaphore);
664
 
                PR_ASSERT(err == noErr);
665
 
        }
666
 
#endif
667
 
}
668
 
 
669
 
void TermIdleSemaphore()
670
 
{
671
 
#if TARGET_CARBON
672
 
        if (gUseIdleSemaphore)
673
 
        {
674
 
                OSStatus  err = MPDeleteSemaphore(gIdleSemaphore);
675
 
                PR_ASSERT(err == noErr);
676
 
                gUseIdleSemaphore = NULL;
677
 
        }
678
 
#endif
679
 
}
680
 
 
681
 
 
682
 
void WaitOnIdleSemaphore(PRIntervalTime timeout)
683
 
{
684
 
#if TARGET_CARBON
685
 
        if (gUseIdleSemaphore)
686
 
        {
687
 
                OSStatus  err = MPWaitOnSemaphore(gIdleSemaphore, kDurationMillisecond * PR_IntervalToMilliseconds(timeout));
688
 
                PR_ASSERT(err == noErr);
689
 
        }
690
 
        else
691
 
#endif
692
 
        {
693
 
                EventRecord   theEvent;
694
 
                /*
695
 
                ** Calling WaitNextEvent() here is suboptimal. This routine should
696
 
                ** pause the process until IO or the timeout occur, yielding time to
697
 
                ** other processes on operating systems that require this (Mac OS classic).
698
 
                ** WaitNextEvent() may incur too much latency, and has other problems,
699
 
                ** such as the potential to drop suspend/resume events.
700
 
                */
701
 
                (void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
702
 
        }
703
 
}
704
 
 
705
 
 
706
 
void SignalIdleSemaphore()
707
 
{
708
 
#if TARGET_CARBON
709
 
        if (gUseIdleSemaphore)
710
 
        {
711
 
                // often we won't be waiting on the semaphore here, so ignore any errors
712
 
                (void)MPSignalSemaphore(gIdleSemaphore);
713
 
        }
714
 
        else
715
 
#endif
716
 
        {
717
 
                WakeUpProcess(&gApplicationProcess);
718
 
        }
719
 
}
720
 
 
721