~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/libs/xpcom18a4/nsprpub/pr/src/threads/combined/pruthr.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

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
#include <signal.h>
 
40
#include <string.h>
 
41
 
 
42
#if defined(WIN95)                                                                         
 
43
/*
 
44
** Some local variables report warnings on Win95 because the code paths
 
45
** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
 
46
** The pragma suppresses the warning.
 
47
**
 
48
*/
 
49
#pragma warning(disable : 4101)
 
50
#endif          
 
51
 
 
52
#if defined(XP_MAC)
 
53
#include <LowMem.h>
 
54
#endif
 
55
 
 
56
/* _pr_activeLock protects the following global variables */
 
57
PRLock *_pr_activeLock;
 
58
PRInt32 _pr_primordialExitCount;   /* In PR_Cleanup(), the primordial thread
 
59
                    * waits until all other user (non-system)
 
60
                    * threads have terminated before it exits.
 
61
                    * So whenever we decrement _pr_userActive,
 
62
                    * it is compared with
 
63
                    * _pr_primordialExitCount.
 
64
                    * If the primordial thread is a system
 
65
                    * thread, then _pr_primordialExitCount
 
66
                    * is 0.  If the primordial thread is
 
67
                    * itself a user thread, then 
 
68
                    * _pr_primordialThread is 1.
 
69
                    */
 
70
PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to
 
71
                    * _pr_primordialExitCount, this condition
 
72
                    * variable is notified.
 
73
                    */
 
74
 
 
75
PRLock *_pr_deadQLock;
 
76
PRUint32 _pr_numNativeDead;
 
77
PRUint32 _pr_numUserDead;
 
78
PRCList _pr_deadNativeQ;
 
79
PRCList _pr_deadUserQ;
 
80
 
 
81
PRUint32 _pr_join_counter;
 
82
 
 
83
PRUint32 _pr_local_threads;
 
84
PRUint32 _pr_global_threads;
 
85
 
 
86
PRBool suspendAllOn = PR_FALSE;
 
87
PRThread *suspendAllThread = NULL;
 
88
 
 
89
extern PRCList _pr_active_global_threadQ;
 
90
extern PRCList _pr_active_local_threadQ;
 
91
 
 
92
static void _PR_DecrActiveThreadCount(PRThread *thread);
 
93
static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *);
 
94
static void _PR_InitializeNativeStack(PRThreadStack *ts);
 
95
static void _PR_InitializeRecycledThread(PRThread *thread);
 
96
static void _PR_UserRunThread(void);
 
97
 
 
98
void _PR_InitThreads(PRThreadType type, PRThreadPriority priority,
 
99
    PRUintn maxPTDs)
 
100
{
 
101
#if defined(XP_MAC)
 
102
#pragma unused (maxPTDs)
 
103
#endif
 
104
 
 
105
    PRThread *thread;
 
106
    PRThreadStack *stack;
 
107
 
 
108
    _pr_terminationCVLock = PR_NewLock();
 
109
    _pr_activeLock = PR_NewLock();
 
110
 
 
111
#ifndef HAVE_CUSTOM_USER_THREADS
 
112
    stack = PR_NEWZAP(PRThreadStack);
 
113
#ifdef HAVE_STACK_GROWING_UP
 
114
    stack->stackTop = (char*) ((((long)&type) >> _pr_pageShift)
 
115
                  << _pr_pageShift);
 
116
#else
 
117
#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS)
 
118
    stack->stackTop = (char*) &thread;
 
119
#elif defined(XP_MAC)
 
120
    stack->stackTop = (char*) LMGetCurStackBase();
 
121
#else
 
122
    stack->stackTop = (char*) ((((long)&type + _pr_pageSize - 1)
 
123
                >> _pr_pageShift) << _pr_pageShift);
 
124
#endif
 
125
#endif
 
126
#else
 
127
    /* If stack is NULL, we're using custom user threads like NT fibers. */
 
128
    stack = PR_NEWZAP(PRThreadStack);
 
129
    if (stack) {
 
130
        stack->stackSize = 0;
 
131
        _PR_InitializeNativeStack(stack);
 
132
    }
 
133
#endif /* HAVE_CUSTOM_USER_THREADS */
 
134
 
 
135
    thread = _PR_AttachThread(type, priority, stack);
 
136
    if (thread) {
 
137
        _PR_MD_SET_CURRENT_THREAD(thread);
 
138
 
 
139
        if (type == PR_SYSTEM_THREAD) {
 
140
            thread->flags = _PR_SYSTEM;
 
141
            _pr_systemActive++;
 
142
            _pr_primordialExitCount = 0;
 
143
        } else {
 
144
            _pr_userActive++;
 
145
            _pr_primordialExitCount = 1;
 
146
        }
 
147
    thread->no_sched = 1;
 
148
    _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock);
 
149
    }
 
150
 
 
151
    if (!thread) PR_Abort();
 
152
#ifdef _PR_LOCAL_THREADS_ONLY
 
153
    thread->flags |= _PR_PRIMORDIAL;
 
154
#else
 
155
    thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE;
 
156
#endif
 
157
 
 
158
    /*
 
159
     * Needs _PR_PRIMORDIAL flag set before calling
 
160
     * _PR_MD_INIT_THREAD()
 
161
     */
 
162
    if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
 
163
        /*
 
164
         * XXX do what?
 
165
         */
 
166
    }
 
167
 
 
168
    if (_PR_IS_NATIVE_THREAD(thread)) {
 
169
        PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
 
170
        _pr_global_threads++;
 
171
    } else {
 
172
        PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
 
173
        _pr_local_threads++;
 
174
    }
 
175
 
 
176
    _pr_recycleThreads = 0;
 
177
    _pr_deadQLock = PR_NewLock();
 
178
    _pr_numNativeDead = 0;
 
179
    _pr_numUserDead = 0;
 
180
    PR_INIT_CLIST(&_pr_deadNativeQ);
 
181
    PR_INIT_CLIST(&_pr_deadUserQ);
 
182
}
 
183
 
 
184
void _PR_CleanupThreads(void)
 
185
{
 
186
    if (_pr_terminationCVLock) {
 
187
        PR_DestroyLock(_pr_terminationCVLock);
 
188
        _pr_terminationCVLock = NULL;
 
189
    }
 
190
    if (_pr_activeLock) {
 
191
        PR_DestroyLock(_pr_activeLock);
 
192
        _pr_activeLock = NULL;
 
193
    }
 
194
    if (_pr_primordialExitCVar) {
 
195
        PR_DestroyCondVar(_pr_primordialExitCVar);
 
196
        _pr_primordialExitCVar = NULL;
 
197
    }
 
198
    /* TODO _pr_dead{Native,User}Q need to be deleted */
 
199
    if (_pr_deadQLock) {
 
200
        PR_DestroyLock(_pr_deadQLock);
 
201
        _pr_deadQLock = NULL;
 
202
    }
 
203
}
 
204
 
 
205
/*
 
206
** Initialize a stack for a native thread
 
207
*/
 
208
static void _PR_InitializeNativeStack(PRThreadStack *ts)
 
209
{
 
210
    if( ts && (ts->stackTop == 0) ) {
 
211
        ts->allocSize = ts->stackSize;
 
212
 
 
213
        /*
 
214
        ** Setup stackTop and stackBottom values.
 
215
        */
 
216
#ifdef HAVE_STACK_GROWING_UP
 
217
    ts->allocBase = (char*) ((((long)&ts) >> _pr_pageShift)
 
218
                  << _pr_pageShift);
 
219
        ts->stackBottom = ts->allocBase + ts->stackSize;
 
220
        ts->stackTop = ts->allocBase;
 
221
#else
 
222
        ts->allocBase = (char*) ((((long)&ts + _pr_pageSize - 1)
 
223
                >> _pr_pageShift) << _pr_pageShift);
 
224
        ts->stackTop    = ts->allocBase;
 
225
        ts->stackBottom = ts->allocBase - ts->stackSize;
 
226
#endif
 
227
    }
 
228
}
 
229
 
 
230
void _PR_NotifyJoinWaiters(PRThread *thread)
 
231
{
 
232
    /*
 
233
    ** Handle joinable threads.  Change the state to waiting for join.
 
234
    ** Remove from our run Q and put it on global waiting to join Q.
 
235
    ** Notify on our "termination" condition variable so that joining
 
236
    ** thread will know about our termination.  Switch our context and
 
237
    ** come back later on to continue the cleanup.
 
238
    */    
 
239
    PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
 
240
    if (thread->term != NULL) {
 
241
        PR_Lock(_pr_terminationCVLock);
 
242
        _PR_THREAD_LOCK(thread);
 
243
        thread->state = _PR_JOIN_WAIT;
 
244
        if ( !_PR_IS_NATIVE_THREAD(thread) ) {
 
245
            _PR_MISCQ_LOCK(thread->cpu);
 
246
            _PR_ADD_JOINQ(thread, thread->cpu);
 
247
            _PR_MISCQ_UNLOCK(thread->cpu);
 
248
        }
 
249
        _PR_THREAD_UNLOCK(thread);
 
250
        PR_NotifyCondVar(thread->term);
 
251
        PR_Unlock(_pr_terminationCVLock);
 
252
        _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
 
253
        PR_ASSERT(thread->state != _PR_JOIN_WAIT);
 
254
    }
 
255
 
 
256
}
 
257
 
 
258
/*
 
259
 * Zero some of the data members of a recycled thread.
 
260
 *
 
261
 * Note that we can do this either when a dead thread is added to
 
262
 * the dead thread queue or when it is reused.  Here, we are doing
 
263
 * this lazily, when the thread is reused in _PR_CreateThread().
 
264
 */
 
265
static void _PR_InitializeRecycledThread(PRThread *thread)
 
266
{
 
267
    /*
 
268
     * Assert that the following data members are already zeroed
 
269
     * by _PR_CleanupThread().
 
270
     */
 
271
#ifdef DEBUG
 
272
    if (thread->privateData) {
 
273
        unsigned int i;
 
274
        for (i = 0; i < thread->tpdLength; i++) {
 
275
            PR_ASSERT(thread->privateData[i] == NULL);
 
276
        }
 
277
    }
 
278
#endif
 
279
    PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0);
 
280
    PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0);
 
281
    PR_ASSERT(thread->errorStringLength == 0);
 
282
 
 
283
    /* Reset data members in thread structure */
 
284
    thread->errorCode = thread->osErrorCode = 0;
 
285
    thread->io_pending = thread->io_suspended = PR_FALSE;
 
286
    thread->environment = 0;
 
287
    PR_INIT_CLIST(&thread->lockList);
 
288
}
 
289
 
 
290
PRStatus _PR_RecycleThread(PRThread *thread)
 
291
{
 
292
    if ( _PR_IS_NATIVE_THREAD(thread) &&
 
293
            _PR_NUM_DEADNATIVE < _pr_recycleThreads) {
 
294
        _PR_DEADQ_LOCK;
 
295
        PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ);
 
296
        _PR_INC_DEADNATIVE;
 
297
        _PR_DEADQ_UNLOCK;
 
298
    return (PR_SUCCESS);
 
299
    } else if ( !_PR_IS_NATIVE_THREAD(thread) &&
 
300
                _PR_NUM_DEADUSER < _pr_recycleThreads) {
 
301
        _PR_DEADQ_LOCK;
 
302
        PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ);
 
303
        _PR_INC_DEADUSER;
 
304
        _PR_DEADQ_UNLOCK;
 
305
    return (PR_SUCCESS);
 
306
    }
 
307
    return (PR_FAILURE);
 
308
}
 
309
 
 
310
/*
 
311
 * Decrement the active thread count, either _pr_systemActive or
 
312
 * _pr_userActive, depending on whether the thread is a system thread
 
313
 * or a user thread.  If all the user threads, except possibly
 
314
 * the primordial thread, have terminated, we notify the primordial
 
315
 * thread of this condition.
 
316
 *
 
317
 * Since this function will lock _pr_activeLock, do not call this
 
318
 * function while holding the _pr_activeLock lock, as this will result
 
319
 * in a deadlock.
 
320
 */
 
321
 
 
322
static void
 
323
_PR_DecrActiveThreadCount(PRThread *thread)
 
324
{
 
325
    PR_Lock(_pr_activeLock);
 
326
    if (thread->flags & _PR_SYSTEM) {
 
327
        _pr_systemActive--;
 
328
    } else {
 
329
        _pr_userActive--;
 
330
        if (_pr_userActive == _pr_primordialExitCount) {
 
331
            PR_NotifyCondVar(_pr_primordialExitCVar);
 
332
        }
 
333
    }
 
334
    PR_Unlock(_pr_activeLock);
 
335
}
 
336
 
 
337
/*
 
338
** Detach thread structure
 
339
*/
 
340
static void
 
341
_PR_DestroyThread(PRThread *thread)
 
342
{
 
343
    _PR_MD_FREE_LOCK(&thread->threadLock);
 
344
    PR_DELETE(thread);
 
345
}
 
346
 
 
347
void
 
348
_PR_NativeDestroyThread(PRThread *thread)
 
349
{
 
350
    if(thread->term) {
 
351
        PR_DestroyCondVar(thread->term);
 
352
        thread->term = 0;
 
353
    }
 
354
    if (NULL != thread->privateData) {
 
355
        PR_ASSERT(0 != thread->tpdLength);
 
356
        PR_DELETE(thread->privateData);
 
357
        thread->tpdLength = 0;
 
358
    }
 
359
    PR_DELETE(thread->stack);
 
360
    _PR_DestroyThread(thread);
 
361
}
 
362
 
 
363
void
 
364
_PR_UserDestroyThread(PRThread *thread)
 
365
{
 
366
    if(thread->term) {
 
367
        PR_DestroyCondVar(thread->term);
 
368
        thread->term = 0;
 
369
    }
 
370
    if (NULL != thread->privateData) {
 
371
        PR_ASSERT(0 != thread->tpdLength);
 
372
        PR_DELETE(thread->privateData);
 
373
        thread->tpdLength = 0;
 
374
    }
 
375
    _PR_MD_FREE_LOCK(&thread->threadLock);
 
376
    if (thread->threadAllocatedOnStack == 1) {
 
377
        _PR_MD_CLEAN_THREAD(thread);
 
378
        /*
 
379
         *  Because the no_sched field is set, this thread/stack will
 
380
         *  will not be re-used until the flag is cleared by the thread
 
381
         *  we will context switch to.
 
382
         */
 
383
        _PR_FreeStack(thread->stack);
 
384
    } else {
 
385
#ifdef WINNT
 
386
        _PR_MD_CLEAN_THREAD(thread);
 
387
#else
 
388
        /*
 
389
         * This assertion does not apply to NT.  On NT, every fiber
 
390
         * has its threadAllocatedOnStack equal to 0.  Elsewhere,
 
391
         * only the primordial thread has its threadAllocatedOnStack
 
392
         * equal to 0.
 
393
         */
 
394
        PR_ASSERT(thread->flags & _PR_PRIMORDIAL);
 
395
#endif
 
396
    }
 
397
}
 
398
 
 
399
 
 
400
/*
 
401
** Run a thread's start function. When the start function returns the
 
402
** thread is done executing and no longer needs the CPU. If there are no
 
403
** more user threads running then we can exit the program.
 
404
*/
 
405
void _PR_NativeRunThread(void *arg)
 
406
{
 
407
    PRThread *thread = (PRThread *)arg;
 
408
 
 
409
    _PR_MD_SET_CURRENT_THREAD(thread);
 
410
 
 
411
    _PR_MD_SET_CURRENT_CPU(NULL);
 
412
 
 
413
    /* Set up the thread stack information */
 
414
    _PR_InitializeNativeStack(thread->stack);
 
415
 
 
416
    /* Set up the thread md information */
 
417
    if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
 
418
        /*
 
419
         * thread failed to initialize itself, possibly due to
 
420
         * failure to allocate per-thread resources
 
421
         */
 
422
        return;
 
423
    }
 
424
 
 
425
    while(1) {
 
426
        thread->state = _PR_RUNNING;
 
427
 
 
428
        /*
 
429
         * Add to list of active threads
 
430
         */
 
431
        PR_Lock(_pr_activeLock);
 
432
        PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
 
433
        _pr_global_threads++;
 
434
        PR_Unlock(_pr_activeLock);
 
435
 
 
436
        (*thread->startFunc)(thread->arg);
 
437
 
 
438
        /*
 
439
         * The following two assertions are meant for NT asynch io.
 
440
         *
 
441
         * The thread should have no asynch io in progress when it
 
442
         * exits, otherwise the overlapped buffer, which is part of
 
443
         * the thread structure, would become invalid.
 
444
         */
 
445
        PR_ASSERT(thread->io_pending == PR_FALSE);
 
446
        /*
 
447
         * This assertion enforces the programming guideline that
 
448
         * if an io function times out or is interrupted, the thread
 
449
         * should close the fd to force the asynch io to abort
 
450
         * before it exits.  Right now, closing the fd is the only
 
451
         * way to clear the io_suspended flag.
 
452
         */
 
453
        PR_ASSERT(thread->io_suspended == PR_FALSE);
 
454
 
 
455
        /*
 
456
         * remove thread from list of active threads
 
457
         */
 
458
        PR_Lock(_pr_activeLock);
 
459
        PR_REMOVE_LINK(&thread->active);
 
460
        _pr_global_threads--;
 
461
        PR_Unlock(_pr_activeLock);
 
462
 
 
463
        PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
 
464
 
 
465
        /* All done, time to go away */
 
466
        _PR_CleanupThread(thread);
 
467
 
 
468
        _PR_NotifyJoinWaiters(thread);
 
469
 
 
470
        _PR_DecrActiveThreadCount(thread);
 
471
 
 
472
        thread->state = _PR_DEAD_STATE;
 
473
 
 
474
        if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
 
475
                        PR_FAILURE)) {
 
476
            /*
 
477
             * thread not recycled
 
478
             * platform-specific thread exit processing
 
479
             *        - for stuff like releasing native-thread resources, etc.
 
480
             */
 
481
            _PR_MD_EXIT_THREAD(thread);
 
482
            /*
 
483
             * Free memory allocated for the thread
 
484
             */
 
485
            _PR_NativeDestroyThread(thread);
 
486
            /*
 
487
             * thread gone, cannot de-reference thread now
 
488
             */
 
489
            return;
 
490
        }
 
491
 
 
492
        /* Now wait for someone to activate us again... */
 
493
        _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
 
494
    }
 
495
}
 
496
 
 
497
static void _PR_UserRunThread(void)
 
498
{
 
499
    PRThread *thread = _PR_MD_CURRENT_THREAD();
 
500
    PRIntn is;
 
501
 
 
502
    if (_MD_LAST_THREAD())
 
503
    _MD_LAST_THREAD()->no_sched = 0;
 
504
 
 
505
#ifdef HAVE_CUSTOM_USER_THREADS
 
506
    if (thread->stack == NULL) {
 
507
        thread->stack = PR_NEWZAP(PRThreadStack);
 
508
        _PR_InitializeNativeStack(thread->stack);
 
509
    }
 
510
#endif /* HAVE_CUSTOM_USER_THREADS */
 
511
 
 
512
    while(1) {
 
513
        /* Run thread main */
 
514
        if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_MD_SET_INTSOFF(0);
 
515
 
 
516
    /*
 
517
     * Add to list of active threads
 
518
     */
 
519
    if (!(thread->flags & _PR_IDLE_THREAD)) {
 
520
        PR_Lock(_pr_activeLock);
 
521
        PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
 
522
        _pr_local_threads++;
 
523
        PR_Unlock(_pr_activeLock);
 
524
    }
 
525
 
 
526
        (*thread->startFunc)(thread->arg);
 
527
 
 
528
        /*
 
529
         * The following two assertions are meant for NT asynch io.
 
530
         *
 
531
         * The thread should have no asynch io in progress when it
 
532
         * exits, otherwise the overlapped buffer, which is part of
 
533
         * the thread structure, would become invalid.
 
534
         */
 
535
        PR_ASSERT(thread->io_pending == PR_FALSE);
 
536
        /*
 
537
         * This assertion enforces the programming guideline that
 
538
         * if an io function times out or is interrupted, the thread
 
539
         * should close the fd to force the asynch io to abort
 
540
         * before it exits.  Right now, closing the fd is the only
 
541
         * way to clear the io_suspended flag.
 
542
         */
 
543
        PR_ASSERT(thread->io_suspended == PR_FALSE);
 
544
 
 
545
        PR_Lock(_pr_activeLock);
 
546
    /*
 
547
     * remove thread from list of active threads
 
548
     */
 
549
    if (!(thread->flags & _PR_IDLE_THREAD)) {
 
550
           PR_REMOVE_LINK(&thread->active);
 
551
        _pr_local_threads--;
 
552
    }
 
553
    PR_Unlock(_pr_activeLock);
 
554
        PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
 
555
 
 
556
        /* All done, time to go away */
 
557
        _PR_CleanupThread(thread);
 
558
 
 
559
        _PR_INTSOFF(is);    
 
560
 
 
561
        _PR_NotifyJoinWaiters(thread);
 
562
 
 
563
    _PR_DecrActiveThreadCount(thread);
 
564
 
 
565
        thread->state = _PR_DEAD_STATE;
 
566
 
 
567
        if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
 
568
                        PR_FAILURE)) {
 
569
            /*
 
570
            ** Destroy the thread resources
 
571
            */
 
572
        _PR_UserDestroyThread(thread);
 
573
        }
 
574
 
 
575
        /*
 
576
        ** Find another user thread to run. This cpu has finished the
 
577
        ** previous threads main and is now ready to run another thread.
 
578
        */
 
579
        {
 
580
            PRInt32 is;
 
581
            _PR_INTSOFF(is);
 
582
            _PR_MD_SWITCH_CONTEXT(thread);
 
583
        }
 
584
 
 
585
        /* Will land here when we get scheduled again if we are recycling... */
 
586
    }
 
587
}
 
588
 
 
589
void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri)
 
590
{
 
591
    PRThread *me = _PR_MD_CURRENT_THREAD();
 
592
    PRIntn is;
 
593
 
 
594
    if ( _PR_IS_NATIVE_THREAD(thread) ) {
 
595
        _PR_MD_SET_PRIORITY(&(thread->md), newPri);
 
596
        return;
 
597
    }
 
598
 
 
599
    if (!_PR_IS_NATIVE_THREAD(me))
 
600
    _PR_INTSOFF(is);
 
601
    _PR_THREAD_LOCK(thread);
 
602
    if (newPri != thread->priority) {
 
603
    _PRCPU *cpu = thread->cpu;
 
604
 
 
605
    switch (thread->state) {
 
606
      case _PR_RUNNING:
 
607
        /* Change my priority */
 
608
 
 
609
            _PR_RUNQ_LOCK(cpu);
 
610
        thread->priority = newPri;
 
611
        if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) {
 
612
            if (!_PR_IS_NATIVE_THREAD(me))
 
613
                    _PR_SET_RESCHED_FLAG();
 
614
        }
 
615
            _PR_RUNQ_UNLOCK(cpu);
 
616
        break;
 
617
 
 
618
      case _PR_RUNNABLE:
 
619
 
 
620
        _PR_RUNQ_LOCK(cpu);
 
621
            /* Move to different runQ */
 
622
            _PR_DEL_RUNQ(thread);
 
623
            thread->priority = newPri;
 
624
            PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
 
625
            _PR_ADD_RUNQ(thread, cpu, newPri);
 
626
        _PR_RUNQ_UNLOCK(cpu);
 
627
 
 
628
            if (newPri > me->priority) {
 
629
            if (!_PR_IS_NATIVE_THREAD(me))
 
630
                    _PR_SET_RESCHED_FLAG();
 
631
            }
 
632
 
 
633
        break;
 
634
 
 
635
      case _PR_LOCK_WAIT:
 
636
      case _PR_COND_WAIT:
 
637
      case _PR_IO_WAIT:
 
638
      case _PR_SUSPENDED:
 
639
 
 
640
        thread->priority = newPri;
 
641
        break;
 
642
    }
 
643
    }
 
644
    _PR_THREAD_UNLOCK(thread);
 
645
    if (!_PR_IS_NATIVE_THREAD(me))
 
646
    _PR_INTSON(is);
 
647
}
 
648
 
 
649
/*
 
650
** Suspend the named thread and copy its gc registers into regBuf
 
651
*/
 
652
static void _PR_Suspend(PRThread *thread)
 
653
{
 
654
    PRIntn is;
 
655
    PRThread *me = _PR_MD_CURRENT_THREAD();
 
656
 
 
657
    PR_ASSERT(thread != me);
 
658
    PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu));
 
659
 
 
660
    if (!_PR_IS_NATIVE_THREAD(me))
 
661
        _PR_INTSOFF(is);
 
662
    _PR_THREAD_LOCK(thread);
 
663
    switch (thread->state) {
 
664
      case _PR_RUNNABLE:
 
665
        if (!_PR_IS_NATIVE_THREAD(thread)) {
 
666
            _PR_RUNQ_LOCK(thread->cpu);
 
667
            _PR_DEL_RUNQ(thread);
 
668
            _PR_RUNQ_UNLOCK(thread->cpu);
 
669
 
 
670
            _PR_MISCQ_LOCK(thread->cpu);
 
671
            _PR_ADD_SUSPENDQ(thread, thread->cpu);
 
672
            _PR_MISCQ_UNLOCK(thread->cpu);
 
673
        } else {
 
674
            /*
 
675
             * Only LOCAL threads are suspended by _PR_Suspend
 
676
             */
 
677
             PR_ASSERT(0);
 
678
        }
 
679
        thread->state = _PR_SUSPENDED;
 
680
        break;
 
681
 
 
682
      case _PR_RUNNING:
 
683
        /*
 
684
         * The thread being suspended should be a LOCAL thread with
 
685
         * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
 
686
         */
 
687
        PR_ASSERT(0);
 
688
        break;
 
689
 
 
690
      case _PR_LOCK_WAIT:
 
691
      case _PR_IO_WAIT:
 
692
      case _PR_COND_WAIT:
 
693
        if (_PR_IS_NATIVE_THREAD(thread)) {
 
694
            _PR_MD_SUSPEND_THREAD(thread);
 
695
    }
 
696
        thread->flags |= _PR_SUSPENDING;
 
697
        break;
 
698
 
 
699
      default:
 
700
        PR_Abort();
 
701
    }
 
702
    _PR_THREAD_UNLOCK(thread);
 
703
    if (!_PR_IS_NATIVE_THREAD(me))
 
704
    _PR_INTSON(is);
 
705
}
 
706
 
 
707
static void _PR_Resume(PRThread *thread)
 
708
{
 
709
    PRThreadPriority pri;
 
710
    PRIntn is;
 
711
    PRThread *me = _PR_MD_CURRENT_THREAD();
 
712
 
 
713
    if (!_PR_IS_NATIVE_THREAD(me))
 
714
    _PR_INTSOFF(is);
 
715
    _PR_THREAD_LOCK(thread);
 
716
    switch (thread->state) {
 
717
      case _PR_SUSPENDED:
 
718
        thread->state = _PR_RUNNABLE;
 
719
        thread->flags &= ~_PR_SUSPENDING;
 
720
        if (!_PR_IS_NATIVE_THREAD(thread)) {
 
721
            _PR_MISCQ_LOCK(thread->cpu);
 
722
            _PR_DEL_SUSPENDQ(thread);
 
723
            _PR_MISCQ_UNLOCK(thread->cpu);
 
724
 
 
725
            pri = thread->priority;
 
726
 
 
727
            _PR_RUNQ_LOCK(thread->cpu);
 
728
            _PR_ADD_RUNQ(thread, thread->cpu, pri);
 
729
            _PR_RUNQ_UNLOCK(thread->cpu);
 
730
 
 
731
            if (pri > _PR_MD_CURRENT_THREAD()->priority) {
 
732
                if (!_PR_IS_NATIVE_THREAD(me))
 
733
                    _PR_SET_RESCHED_FLAG();
 
734
            }
 
735
        } else {
 
736
            PR_ASSERT(0);
 
737
        }
 
738
        break;
 
739
 
 
740
      case _PR_IO_WAIT:
 
741
      case _PR_COND_WAIT:
 
742
        thread->flags &= ~_PR_SUSPENDING;
 
743
/*      PR_ASSERT(thread->wait.monitor->stickyCount == 0); */
 
744
        break;
 
745
 
 
746
      case _PR_LOCK_WAIT: 
 
747
      {
 
748
        PRLock *wLock = thread->wait.lock;
 
749
 
 
750
        thread->flags &= ~_PR_SUSPENDING;
 
751
 
 
752
        _PR_LOCK_LOCK(wLock);
 
753
        if (thread->wait.lock->owner == 0) {
 
754
            _PR_UnblockLockWaiter(thread->wait.lock);
 
755
        }
 
756
        _PR_LOCK_UNLOCK(wLock);
 
757
        break;
 
758
      }
 
759
      case _PR_RUNNABLE:
 
760
        break;
 
761
      case _PR_RUNNING:
 
762
        /*
 
763
         * The thread being suspended should be a LOCAL thread with
 
764
         * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
 
765
         */
 
766
        PR_ASSERT(0);
 
767
        break;
 
768
 
 
769
      default:
 
770
    /*
 
771
     * thread should have been in one of the above-listed blocked states
 
772
     * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE)
 
773
     */
 
774
        PR_Abort();
 
775
    }
 
776
    _PR_THREAD_UNLOCK(thread);
 
777
    if (!_PR_IS_NATIVE_THREAD(me))
 
778
        _PR_INTSON(is);
 
779
 
 
780
}
 
781
 
 
782
#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
 
783
static PRThread *get_thread(_PRCPU *cpu, PRBool *wakeup_cpus)
 
784
{
 
785
    PRThread *thread;
 
786
    PRIntn pri;
 
787
    PRUint32 r;
 
788
    PRCList *qp;
 
789
    PRIntn priMin, priMax;
 
790
 
 
791
    _PR_RUNQ_LOCK(cpu);
 
792
    r = _PR_RUNQREADYMASK(cpu);
 
793
    if (r==0) {
 
794
        priMin = priMax = PR_PRIORITY_FIRST;
 
795
    } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
 
796
        priMin = priMax = PR_PRIORITY_NORMAL;
 
797
    } else {
 
798
        priMin = PR_PRIORITY_FIRST;
 
799
        priMax = PR_PRIORITY_LAST;
 
800
    }
 
801
    thread = NULL;
 
802
    for (pri = priMax; pri >= priMin ; pri-- ) {
 
803
    if (r & (1 << pri)) {
 
804
            for (qp = _PR_RUNQ(cpu)[pri].next; 
 
805
                 qp != &_PR_RUNQ(cpu)[pri];
 
806
                 qp = qp->next) {
 
807
                thread = _PR_THREAD_PTR(qp);
 
808
                /*
 
809
                * skip non-schedulable threads
 
810
                */
 
811
                PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
 
812
                if (thread->no_sched) {
 
813
                    thread = NULL;
 
814
                    /*
 
815
                     * Need to wakeup cpus to avoid missing a
 
816
                     * runnable thread
 
817
                     * Waking up all CPU's need happen only once.
 
818
                     */
 
819
 
 
820
                    *wakeup_cpus = PR_TRUE;
 
821
                    continue;
 
822
                } else if (thread->flags & _PR_BOUND_THREAD) {
 
823
                    /*
 
824
                     * Thread bound to cpu 0
 
825
                     */
 
826
 
 
827
                    thread = NULL;
 
828
#ifdef IRIX
 
829
                                        _PR_MD_WAKEUP_PRIMORDIAL_CPU();
 
830
#endif
 
831
                    continue;
 
832
                } else if (thread->io_pending == PR_TRUE) {
 
833
                    /*
 
834
                     * A thread that is blocked for I/O needs to run
 
835
                     * on the same cpu on which it was blocked. This is because
 
836
                     * the cpu's ioq is accessed without lock protection and scheduling
 
837
                     * the thread on a different cpu would preclude this optimization.
 
838
                     */
 
839
                    thread = NULL;
 
840
                    continue;
 
841
                } else {
 
842
                    /* Pull thread off of its run queue */
 
843
                    _PR_DEL_RUNQ(thread);
 
844
                    _PR_RUNQ_UNLOCK(cpu);
 
845
                    return(thread);
 
846
                }
 
847
            }
 
848
        }
 
849
        thread = NULL;
 
850
    }
 
851
    _PR_RUNQ_UNLOCK(cpu);
 
852
    return(thread);
 
853
}
 
854
#endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */
 
855
 
 
856
/*
 
857
** Schedule this native thread by finding the highest priority nspr
 
858
** thread that is ready to run.
 
859
**
 
860
** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls
 
861
**       PR_Schedule() rather than calling PR_Schedule.  Otherwise if there
 
862
**       is initialization required for switching from SWITCH_CONTEXT,
 
863
**       it will not get done!
 
864
*/
 
865
void _PR_Schedule(void)
 
866
{
 
867
    PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
 
868
    _PRCPU *cpu = _PR_MD_CURRENT_CPU();
 
869
    PRIntn pri;
 
870
    PRUint32 r;
 
871
    PRCList *qp;
 
872
    PRIntn priMin, priMax;
 
873
#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
 
874
    PRBool wakeup_cpus;
 
875
#endif
 
876
 
 
877
    /* Interrupts must be disabled */
 
878
    PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
 
879
 
 
880
    /* Since we are rescheduling, we no longer want to */
 
881
    _PR_CLEAR_RESCHED_FLAG();
 
882
 
 
883
    /*
 
884
    ** Find highest priority thread to run. Bigger priority numbers are
 
885
    ** higher priority threads
 
886
    */
 
887
    _PR_RUNQ_LOCK(cpu);
 
888
    /*
 
889
     *  if we are in SuspendAll mode, can schedule only the thread
 
890
     *    that called PR_SuspendAll
 
891
     *
 
892
     *  The thread may be ready to run now, after completing an I/O
 
893
     *  operation, for example
 
894
     */
 
895
    if ((thread = suspendAllThread) != 0) {
 
896
    if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) {
 
897
            /* Pull thread off of its run queue */
 
898
            _PR_DEL_RUNQ(thread);
 
899
            _PR_RUNQ_UNLOCK(cpu);
 
900
            goto found_thread;
 
901
    } else {
 
902
            thread = NULL;
 
903
            _PR_RUNQ_UNLOCK(cpu);
 
904
            goto idle_thread;
 
905
    }
 
906
    }
 
907
    r = _PR_RUNQREADYMASK(cpu);
 
908
    if (r==0) {
 
909
        priMin = priMax = PR_PRIORITY_FIRST;
 
910
    } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
 
911
        priMin = priMax = PR_PRIORITY_NORMAL;
 
912
    } else {
 
913
        priMin = PR_PRIORITY_FIRST;
 
914
        priMax = PR_PRIORITY_LAST;
 
915
    }
 
916
    thread = NULL;
 
917
    for (pri = priMax; pri >= priMin ; pri-- ) {
 
918
    if (r & (1 << pri)) {
 
919
            for (qp = _PR_RUNQ(cpu)[pri].next; 
 
920
                 qp != &_PR_RUNQ(cpu)[pri];
 
921
                 qp = qp->next) {
 
922
                thread = _PR_THREAD_PTR(qp);
 
923
                /*
 
924
                * skip non-schedulable threads
 
925
                */
 
926
#if !defined(XP_MAC)
 
927
                PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
 
928
#endif
 
929
                if ((thread->no_sched) && (me != thread)){
 
930
                    thread = NULL;
 
931
                    continue;
 
932
                } else {
 
933
                    /* Pull thread off of its run queue */
 
934
                    _PR_DEL_RUNQ(thread);
 
935
                    _PR_RUNQ_UNLOCK(cpu);
 
936
                    goto found_thread;
 
937
                }
 
938
            }
 
939
        }
 
940
        thread = NULL;
 
941
    }
 
942
    _PR_RUNQ_UNLOCK(cpu);
 
943
 
 
944
#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
 
945
 
 
946
    wakeup_cpus = PR_FALSE;
 
947
    _PR_CPU_LIST_LOCK();
 
948
    for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
 
949
        if (cpu != _PR_CPU_PTR(qp)) {
 
950
            if ((thread = get_thread(_PR_CPU_PTR(qp), &wakeup_cpus))
 
951
                                        != NULL) {
 
952
                thread->cpu = cpu;
 
953
                _PR_CPU_LIST_UNLOCK();
 
954
                if (wakeup_cpus == PR_TRUE)
 
955
                    _PR_MD_WAKEUP_CPUS();
 
956
                goto found_thread;
 
957
            }
 
958
        }
 
959
    }
 
960
    _PR_CPU_LIST_UNLOCK();
 
961
    if (wakeup_cpus == PR_TRUE)
 
962
        _PR_MD_WAKEUP_CPUS();
 
963
 
 
964
#endif        /* _PR_LOCAL_THREADS_ONLY */
 
965
 
 
966
idle_thread:
 
967
   /*
 
968
    ** There are no threads to run. Switch to the idle thread
 
969
    */
 
970
    PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing"));
 
971
    thread = _PR_MD_CURRENT_CPU()->idle_thread;
 
972
 
 
973
found_thread:
 
974
    PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) &&
 
975
                    (!(thread->no_sched))));
 
976
 
 
977
    /* Resume the thread */
 
978
    PR_LOG(_pr_sched_lm, PR_LOG_MAX,
 
979
       ("switching to %d[%p]", thread->id, thread));
 
980
    PR_ASSERT(thread->state != _PR_RUNNING);
 
981
    thread->state = _PR_RUNNING;
 
982
 
 
983
    /* If we are on the runq, it just means that we went to sleep on some
 
984
     * resource, and by the time we got here another real native thread had
 
985
     * already given us the resource and put us back on the runqueue 
 
986
     */
 
987
        PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU());
 
988
    if (thread != me) 
 
989
        _PR_MD_RESTORE_CONTEXT(thread);
 
990
#if 0
 
991
    /* XXXMB; with setjmp/longjmp it is impossible to land here, but 
 
992
     * it is not with fibers... Is this a bad thing?  I believe it is 
 
993
     * still safe.
 
994
     */
 
995
    PR_NOT_REACHED("impossible return from schedule");
 
996
#endif
 
997
}
 
998
 
 
999
/*
 
1000
** Attaches a thread.  
 
1001
** Does not set the _PR_MD_CURRENT_THREAD.  
 
1002
** Does not specify the scope of the thread.
 
1003
*/
 
1004
static PRThread *
 
1005
_PR_AttachThread(PRThreadType type, PRThreadPriority priority,
 
1006
    PRThreadStack *stack)
 
1007
{
 
1008
#if defined(XP_MAC)
 
1009
#pragma unused (type)
 
1010
#endif
 
1011
 
 
1012
    PRThread *thread;
 
1013
    char *mem;
 
1014
 
 
1015
    if (priority > PR_PRIORITY_LAST) {
 
1016
        priority = PR_PRIORITY_LAST;
 
1017
    } else if (priority < PR_PRIORITY_FIRST) {
 
1018
        priority = PR_PRIORITY_FIRST;
 
1019
    }
 
1020
 
 
1021
    mem = (char*) PR_CALLOC(sizeof(PRThread));
 
1022
    if (mem) {
 
1023
        thread = (PRThread*) mem;
 
1024
        thread->priority = priority;
 
1025
        thread->stack = stack;
 
1026
        thread->state = _PR_RUNNING;
 
1027
        PR_INIT_CLIST(&thread->lockList);
 
1028
        if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
 
1029
        PR_DELETE(thread);
 
1030
        return 0;
 
1031
    }
 
1032
 
 
1033
        return thread;
 
1034
    }
 
1035
    return 0;
 
1036
}
 
1037
 
 
1038
 
 
1039
 
 
1040
PR_IMPLEMENT(PRThread*) 
 
1041
_PR_NativeCreateThread(PRThreadType type,
 
1042
                     void (*start)(void *arg),
 
1043
                     void *arg,
 
1044
                     PRThreadPriority priority,
 
1045
                     PRThreadScope scope,
 
1046
                     PRThreadState state,
 
1047
                     PRUint32 stackSize,
 
1048
                     PRUint32 flags)
 
1049
{
 
1050
#if defined(XP_MAC)
 
1051
#pragma unused (scope)
 
1052
#endif
 
1053
 
 
1054
    PRThread *thread;
 
1055
 
 
1056
    thread = _PR_AttachThread(type, priority, NULL);
 
1057
 
 
1058
    if (thread) {
 
1059
        PR_Lock(_pr_activeLock);
 
1060
        thread->flags = (flags | _PR_GLOBAL_SCOPE);
 
1061
        thread->id = ++_pr_utid;
 
1062
        if (type == PR_SYSTEM_THREAD) {
 
1063
            thread->flags |= _PR_SYSTEM;
 
1064
            _pr_systemActive++;
 
1065
        } else {
 
1066
            _pr_userActive++;
 
1067
        }
 
1068
        PR_Unlock(_pr_activeLock);
 
1069
 
 
1070
        thread->stack = PR_NEWZAP(PRThreadStack);
 
1071
        if (!thread->stack) {
 
1072
            PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
 
1073
            goto done;
 
1074
        }
 
1075
        thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE;
 
1076
        thread->stack->thr = thread;
 
1077
        thread->startFunc = start;
 
1078
        thread->arg = arg;
 
1079
 
 
1080
        /* 
 
1081
          Set thread flags related to scope and joinable state. If joinable
 
1082
          thread, allocate a "termination" conidition variable.
 
1083
         */
 
1084
        if (state == PR_JOINABLE_THREAD) {
 
1085
            thread->term = PR_NewCondVar(_pr_terminationCVLock);
 
1086
        if (thread->term == NULL) {
 
1087
        PR_DELETE(thread->stack);
 
1088
        goto done;
 
1089
        }
 
1090
        }
 
1091
 
 
1092
    thread->state = _PR_RUNNING;
 
1093
        if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority,
 
1094
            scope,state,stackSize) == PR_SUCCESS) {
 
1095
            return thread;
 
1096
        }
 
1097
        if (thread->term) {
 
1098
            PR_DestroyCondVar(thread->term);
 
1099
            thread->term = NULL;
 
1100
        }
 
1101
    PR_DELETE(thread->stack);
 
1102
    }
 
1103
 
 
1104
done:
 
1105
    if (thread) {
 
1106
    _PR_DecrActiveThreadCount(thread);
 
1107
        _PR_DestroyThread(thread);
 
1108
    }
 
1109
    return NULL;
 
1110
}
 
1111
 
 
1112
/************************************************************************/
 
1113
 
 
1114
PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type,
 
1115
                     void (*start)(void *arg),
 
1116
                     void *arg,
 
1117
                     PRThreadPriority priority,
 
1118
                     PRThreadScope scope,
 
1119
                     PRThreadState state,
 
1120
                     PRUint32 stackSize,
 
1121
                     PRUint32 flags)
 
1122
{
 
1123
    PRThread *me;
 
1124
    PRThread *thread = NULL;
 
1125
    PRThreadStack *stack;
 
1126
    char *top;
 
1127
    PRIntn is;
 
1128
    PRIntn native = 0;
 
1129
    PRIntn useRecycled = 0;
 
1130
    PRBool status;
 
1131
 
 
1132
    /* 
 
1133
    First, pin down the priority.  Not all compilers catch passing out of
 
1134
    range enum here.  If we let bad values thru, priority queues won't work.
 
1135
    */
 
1136
    if (priority > PR_PRIORITY_LAST) {
 
1137
        priority = PR_PRIORITY_LAST;
 
1138
    } else if (priority < PR_PRIORITY_FIRST) {
 
1139
        priority = PR_PRIORITY_FIRST;
 
1140
    }
 
1141
        
 
1142
    if (!_pr_initialized) _PR_ImplicitInitialization();
 
1143
 
 
1144
    if (! (flags & _PR_IDLE_THREAD))
 
1145
        me = _PR_MD_CURRENT_THREAD();
 
1146
 
 
1147
#if    defined(_PR_GLOBAL_THREADS_ONLY)
 
1148
        /*
 
1149
         * can create global threads only
 
1150
         */
 
1151
    if (scope == PR_LOCAL_THREAD)
 
1152
        scope = PR_GLOBAL_THREAD;
 
1153
#endif
 
1154
 
 
1155
        if (_native_threads_only)
 
1156
                scope = PR_GLOBAL_THREAD;
 
1157
 
 
1158
    native = (((scope == PR_GLOBAL_THREAD)|| (scope == PR_GLOBAL_BOUND_THREAD))
 
1159
                                                        && _PR_IS_NATIVE_THREAD_SUPPORTED());
 
1160
 
 
1161
    _PR_ADJUST_STACKSIZE(stackSize);
 
1162
 
 
1163
    if (native) {
 
1164
    /*
 
1165
     * clear the IDLE_THREAD flag which applies to LOCAL
 
1166
     * threads only
 
1167
     */
 
1168
    flags &= ~_PR_IDLE_THREAD;
 
1169
        flags |= _PR_GLOBAL_SCOPE;
 
1170
        if (_PR_NUM_DEADNATIVE > 0) {
 
1171
            _PR_DEADQ_LOCK;
 
1172
 
 
1173
            if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */
 
1174
                _PR_DEADQ_UNLOCK;
 
1175
            } else {
 
1176
                thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next);
 
1177
                PR_REMOVE_LINK(&thread->links);
 
1178
                _PR_DEC_DEADNATIVE;
 
1179
                _PR_DEADQ_UNLOCK;
 
1180
 
 
1181
                _PR_InitializeRecycledThread(thread);
 
1182
                thread->startFunc = start;
 
1183
                thread->arg = arg;
 
1184
            thread->flags = (flags | _PR_GLOBAL_SCOPE);
 
1185
            if (type == PR_SYSTEM_THREAD)
 
1186
            {
 
1187
                thread->flags |= _PR_SYSTEM;
 
1188
                PR_AtomicIncrement(&_pr_systemActive);
 
1189
            }
 
1190
            else PR_AtomicIncrement(&_pr_userActive);
 
1191
 
 
1192
            if (state == PR_JOINABLE_THREAD) {
 
1193
                if (!thread->term) 
 
1194
                       thread->term = PR_NewCondVar(_pr_terminationCVLock);
 
1195
            }
 
1196
        else {
 
1197
                if(thread->term) {
 
1198
                    PR_DestroyCondVar(thread->term);
 
1199
                        thread->term = 0;
 
1200
            }
 
1201
            }
 
1202
 
 
1203
                thread->priority = priority;
 
1204
        _PR_MD_SET_PRIORITY(&(thread->md), priority);
 
1205
        /* XXX what about stackSize? */
 
1206
        thread->state = _PR_RUNNING;
 
1207
                _PR_MD_WAKEUP_WAITER(thread);
 
1208
        return thread;
 
1209
            }
 
1210
        }
 
1211
        thread = _PR_NativeCreateThread(type, start, arg, priority, 
 
1212
                                            scope, state, stackSize, flags);
 
1213
    } else {
 
1214
        if (_PR_NUM_DEADUSER > 0) {
 
1215
            _PR_DEADQ_LOCK;
 
1216
 
 
1217
            if (_PR_NUM_DEADUSER == 0) {  /* thread safe check */
 
1218
                _PR_DEADQ_UNLOCK;
 
1219
            } else {
 
1220
                PRCList *ptr;
 
1221
 
 
1222
                /* Go down list checking for a recycled thread with a 
 
1223
                 * large enough stack.  XXXMB - this has a bad degenerate case.
 
1224
                 */
 
1225
                ptr = _PR_DEADUSERQ.next;
 
1226
                while( ptr != &_PR_DEADUSERQ ) {
 
1227
                    thread = _PR_THREAD_PTR(ptr);
 
1228
                    if ((thread->stack->stackSize >= stackSize) &&
 
1229
                (!thread->no_sched)) {
 
1230
                        PR_REMOVE_LINK(&thread->links);
 
1231
                        _PR_DEC_DEADUSER;
 
1232
                        break;
 
1233
                    } else {
 
1234
                        ptr = ptr->next;
 
1235
                        thread = NULL;
 
1236
                    }
 
1237
                } 
 
1238
 
 
1239
                _PR_DEADQ_UNLOCK;
 
1240
 
 
1241
               if (thread) {
 
1242
                    _PR_InitializeRecycledThread(thread);
 
1243
                    thread->startFunc = start;
 
1244
                    thread->arg = arg;
 
1245
                    thread->priority = priority;
 
1246
            if (state == PR_JOINABLE_THREAD) {
 
1247
            if (!thread->term) 
 
1248
               thread->term = PR_NewCondVar(_pr_terminationCVLock);
 
1249
            } else {
 
1250
            if(thread->term) {
 
1251
               PR_DestroyCondVar(thread->term);
 
1252
                thread->term = 0;
 
1253
            }
 
1254
            }
 
1255
                    useRecycled++;
 
1256
                }
 
1257
            }
 
1258
        } 
 
1259
        if (thread == NULL) {
 
1260
#ifndef HAVE_CUSTOM_USER_THREADS
 
1261
            stack = _PR_NewStack(stackSize);
 
1262
            if (!stack) {
 
1263
                PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
 
1264
                return NULL;
 
1265
            }
 
1266
 
 
1267
            /* Allocate thread object and per-thread data off the top of the stack*/
 
1268
            top = stack->stackTop;
 
1269
#ifdef HAVE_STACK_GROWING_UP
 
1270
            thread = (PRThread*) top;
 
1271
            top = top + sizeof(PRThread);
 
1272
            /*
 
1273
             * Make stack 64-byte aligned
 
1274
             */
 
1275
            if ((PRUptrdiff)top & 0x3f) {
 
1276
                top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f);
 
1277
            }
 
1278
#else
 
1279
            top = top - sizeof(PRThread);
 
1280
            thread = (PRThread*) top;
 
1281
            /*
 
1282
             * Make stack 64-byte aligned
 
1283
             */
 
1284
            if ((PRUptrdiff)top & 0x3f) {
 
1285
                top = (char*)((PRUptrdiff)top & ~0x3f);
 
1286
            }
 
1287
#endif
 
1288
#if defined(GC_LEAK_DETECTOR)
 
1289
            /*
 
1290
             * sorry, it is not safe to allocate the thread on the stack,
 
1291
             * because we assign to this object before the GC can learn
 
1292
             * about this thread. we'll just leak thread objects instead.
 
1293
             */
 
1294
            thread = PR_NEW(PRThread);
 
1295
#endif
 
1296
            stack->thr = thread;
 
1297
            memset(thread, 0, sizeof(PRThread));
 
1298
            thread->threadAllocatedOnStack = 1;
 
1299
#else
 
1300
            thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg);
 
1301
            if (!thread) {
 
1302
                PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
 
1303
                return NULL;
 
1304
            }
 
1305
            thread->threadAllocatedOnStack = 0;
 
1306
            stack = NULL;
 
1307
            top = NULL;
 
1308
#endif
 
1309
 
 
1310
            /* Initialize thread */
 
1311
            thread->tpdLength = 0;
 
1312
            thread->privateData = NULL;
 
1313
            thread->stack = stack;
 
1314
            thread->priority = priority;
 
1315
            thread->startFunc = start;
 
1316
            thread->arg = arg;
 
1317
            PR_INIT_CLIST(&thread->lockList);
 
1318
 
 
1319
            if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
 
1320
                if (thread->threadAllocatedOnStack == 1)
 
1321
                    _PR_FreeStack(thread->stack);
 
1322
                else {
 
1323
                    PR_DELETE(thread);
 
1324
                }
 
1325
                PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
 
1326
                return NULL;
 
1327
            }
 
1328
 
 
1329
            if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
 
1330
                if (thread->threadAllocatedOnStack == 1)
 
1331
                    _PR_FreeStack(thread->stack);
 
1332
                else {
 
1333
                    PR_DELETE(thread->privateData);
 
1334
                    PR_DELETE(thread);
 
1335
                }
 
1336
                PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
 
1337
                return NULL;
 
1338
            }
 
1339
 
 
1340
            _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status);
 
1341
 
 
1342
            if (status == PR_FALSE) {
 
1343
                _PR_MD_FREE_LOCK(&thread->threadLock);
 
1344
                if (thread->threadAllocatedOnStack == 1)
 
1345
                    _PR_FreeStack(thread->stack);
 
1346
                else {
 
1347
                    PR_DELETE(thread->privateData);
 
1348
                    PR_DELETE(thread);
 
1349
                }
 
1350
                return NULL;
 
1351
            }
 
1352
 
 
1353
            /* 
 
1354
              Set thread flags related to scope and joinable state. If joinable
 
1355
              thread, allocate a "termination" condition variable.
 
1356
            */
 
1357
            if (state == PR_JOINABLE_THREAD) {
 
1358
                thread->term = PR_NewCondVar(_pr_terminationCVLock);
 
1359
                if (thread->term == NULL) {
 
1360
                    _PR_MD_FREE_LOCK(&thread->threadLock);
 
1361
                    if (thread->threadAllocatedOnStack == 1)
 
1362
                        _PR_FreeStack(thread->stack);
 
1363
                    else {
 
1364
                        PR_DELETE(thread->privateData);
 
1365
                        PR_DELETE(thread);
 
1366
                    }
 
1367
                    return NULL;
 
1368
                }
 
1369
            }
 
1370
  
 
1371
        }
 
1372
  
 
1373
        /* Update thread type counter */
 
1374
        PR_Lock(_pr_activeLock);
 
1375
        thread->flags = flags;
 
1376
        thread->id = ++_pr_utid;
 
1377
        if (type == PR_SYSTEM_THREAD) {
 
1378
            thread->flags |= _PR_SYSTEM;
 
1379
            _pr_systemActive++;
 
1380
        } else {
 
1381
            _pr_userActive++;
 
1382
        }
 
1383
 
 
1384
        /* Make thread runnable */
 
1385
        thread->state = _PR_RUNNABLE;
 
1386
    /*
 
1387
     * Add to list of active threads
 
1388
     */
 
1389
        PR_Unlock(_pr_activeLock);
 
1390
 
 
1391
        if ((! (thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me) )
 
1392
            thread->cpu = _PR_GetPrimordialCPU();
 
1393
        else
 
1394
            thread->cpu = _PR_MD_CURRENT_CPU();
 
1395
 
 
1396
        PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
 
1397
 
 
1398
        if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) {
 
1399
            _PR_INTSOFF(is);
 
1400
            _PR_RUNQ_LOCK(thread->cpu);
 
1401
            _PR_ADD_RUNQ(thread, thread->cpu, priority);
 
1402
            _PR_RUNQ_UNLOCK(thread->cpu);
 
1403
        }
 
1404
 
 
1405
        if (thread->flags & _PR_IDLE_THREAD) {
 
1406
            /*
 
1407
            ** If the creating thread is a kernel thread, we need to
 
1408
            ** awaken the user thread idle thread somehow; potentially
 
1409
            ** it could be sleeping in its idle loop, and we need to poke
 
1410
            ** it.  To do so, wake the idle thread...  
 
1411
            */
 
1412
            _PR_MD_WAKEUP_WAITER(NULL);
 
1413
        } else if (_PR_IS_NATIVE_THREAD(me)) {
 
1414
            _PR_MD_WAKEUP_WAITER(thread);
 
1415
        }
 
1416
        if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) )
 
1417
            _PR_INTSON(is);
 
1418
    }
 
1419
 
 
1420
    return thread;
 
1421
}
 
1422
 
 
1423
PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type,
 
1424
                     void (*start)(void *arg),
 
1425
                     void *arg,
 
1426
                     PRThreadPriority priority,
 
1427
                     PRThreadScope scope,
 
1428
                     PRThreadState state,
 
1429
                     PRUint32 stackSize)
 
1430
{
 
1431
    return _PR_CreateThread(type, start, arg, priority, scope, state, 
 
1432
                            stackSize, 0);
 
1433
}
 
1434
 
 
1435
/*
 
1436
** Associate a thread object with an existing native thread.
 
1437
**     "type" is the type of thread object to attach
 
1438
**     "priority" is the priority to assign to the thread
 
1439
**     "stack" defines the shape of the threads stack
 
1440
**
 
1441
** This can return NULL if some kind of error occurs, or if memory is
 
1442
** tight.
 
1443
**
 
1444
** This call is not normally needed unless you create your own native
 
1445
** thread. PR_Init does this automatically for the primordial thread.
 
1446
*/
 
1447
PRThread* _PRI_AttachThread(PRThreadType type,
 
1448
    PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags)
 
1449
{
 
1450
    PRThread *thread;
 
1451
 
 
1452
    if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) {
 
1453
        return thread;
 
1454
    }
 
1455
    _PR_MD_SET_CURRENT_THREAD(NULL);
 
1456
 
 
1457
    /* Clear out any state if this thread was attached before */
 
1458
    _PR_MD_SET_CURRENT_CPU(NULL);
 
1459
 
 
1460
    thread = _PR_AttachThread(type, priority, stack);
 
1461
    if (thread) {
 
1462
        PRIntn is;
 
1463
 
 
1464
        _PR_MD_SET_CURRENT_THREAD(thread);
 
1465
 
 
1466
        thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED;
 
1467
 
 
1468
        if (!stack) {
 
1469
            thread->stack = PR_NEWZAP(PRThreadStack);
 
1470
            if (!thread->stack) {
 
1471
                _PR_DestroyThread(thread);
 
1472
                return NULL;
 
1473
            }
 
1474
            thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE;
 
1475
        }
 
1476
        PR_INIT_CLIST(&thread->links);
 
1477
 
 
1478
        if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) {
 
1479
                PR_DELETE(thread->stack);
 
1480
                _PR_DestroyThread(thread);
 
1481
                return NULL;
 
1482
        }
 
1483
 
 
1484
        _PR_MD_SET_CURRENT_CPU(NULL);
 
1485
 
 
1486
        if (_PR_MD_CURRENT_CPU()) {
 
1487
            _PR_INTSOFF(is);
 
1488
            PR_Lock(_pr_activeLock);
 
1489
        }
 
1490
        if (type == PR_SYSTEM_THREAD) {
 
1491
            thread->flags |= _PR_SYSTEM;
 
1492
            _pr_systemActive++;
 
1493
        } else {
 
1494
            _pr_userActive++;
 
1495
        }
 
1496
        if (_PR_MD_CURRENT_CPU()) {
 
1497
            PR_Unlock(_pr_activeLock);
 
1498
            _PR_INTSON(is);
 
1499
        }
 
1500
    }
 
1501
    return thread;
 
1502
}
 
1503
 
 
1504
PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type,
 
1505
    PRThreadPriority priority, PRThreadStack *stack)
 
1506
{
 
1507
#ifdef XP_MAC
 
1508
#pragma unused( type, priority, stack )
 
1509
#endif
 
1510
    return PR_GetCurrentThread();
 
1511
}
 
1512
 
 
1513
PR_IMPLEMENT(void) PR_DetachThread(void)
 
1514
{
 
1515
    /*
 
1516
     * On IRIX, Solaris, and Windows, foreign threads are detached when
 
1517
     * they terminate.
 
1518
     */
 
1519
#if !defined(IRIX) && !defined(WIN32) \
 
1520
        && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))
 
1521
    PRThread *me;
 
1522
    if (_pr_initialized) {
 
1523
        me = _PR_MD_GET_ATTACHED_THREAD();
 
1524
        if ((me != NULL) && (me->flags & _PR_ATTACHED))
 
1525
            _PRI_DetachThread();
 
1526
    }
 
1527
#endif
 
1528
}
 
1529
 
 
1530
void _PRI_DetachThread(void)
 
1531
{
 
1532
    PRThread *me = _PR_MD_CURRENT_THREAD();
 
1533
 
 
1534
        if (me->flags & _PR_PRIMORDIAL) {
 
1535
                /*
 
1536
                 * ignore, if primordial thread
 
1537
                 */
 
1538
                return;
 
1539
        }
 
1540
    PR_ASSERT(me->flags & _PR_ATTACHED);
 
1541
    PR_ASSERT(_PR_IS_NATIVE_THREAD(me));
 
1542
    _PR_CleanupThread(me);
 
1543
    PR_DELETE(me->privateData);
 
1544
 
 
1545
    _PR_DecrActiveThreadCount(me);
 
1546
 
 
1547
    _PR_MD_CLEAN_THREAD(me);
 
1548
    _PR_MD_SET_CURRENT_THREAD(NULL);
 
1549
    if (!me->threadAllocatedOnStack) 
 
1550
        PR_DELETE(me->stack);
 
1551
    _PR_MD_FREE_LOCK(&me->threadLock);
 
1552
    PR_DELETE(me);
 
1553
}
 
1554
 
 
1555
/*
 
1556
** Wait for thread termination:
 
1557
**     "thread" is the target thread 
 
1558
**
 
1559
** This can return PR_FAILURE if no joinable thread could be found 
 
1560
** corresponding to the specified target thread.
 
1561
**
 
1562
** The calling thread is suspended until the target thread completes.
 
1563
** Several threads cannot wait for the same thread to complete; one thread
 
1564
** will complete successfully and others will terminate with an error PR_FAILURE.
 
1565
** The calling thread will not be blocked if the target thread has already
 
1566
** terminated.
 
1567
*/
 
1568
PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread)
 
1569
{
 
1570
    PRIntn is;
 
1571
    PRCondVar *term;
 
1572
    PRThread *me = _PR_MD_CURRENT_THREAD();
 
1573
 
 
1574
    if (!_PR_IS_NATIVE_THREAD(me))
 
1575
        _PR_INTSOFF(is);
 
1576
    term = thread->term;
 
1577
    /* can't join a non-joinable thread */
 
1578
    if (term == NULL) {
 
1579
        PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
 
1580
        goto ErrorExit;
 
1581
    }
 
1582
 
 
1583
    /* multiple threads can't wait on the same joinable thread */
 
1584
    if (term->condQ.next != &term->condQ) {
 
1585
        goto ErrorExit;
 
1586
    }
 
1587
    if (!_PR_IS_NATIVE_THREAD(me))
 
1588
        _PR_INTSON(is);
 
1589
 
 
1590
    /* wait for the target thread's termination cv invariant */
 
1591
    PR_Lock (_pr_terminationCVLock);
 
1592
    while (thread->state != _PR_JOIN_WAIT) {
 
1593
        (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT);
 
1594
    }
 
1595
    (void) PR_Unlock (_pr_terminationCVLock);
 
1596
    
 
1597
    /* 
 
1598
     Remove target thread from global waiting to join Q; make it runnable
 
1599
     again and put it back on its run Q.  When it gets scheduled later in
 
1600
     _PR_RunThread code, it will clean up its stack.
 
1601
    */    
 
1602
    if (!_PR_IS_NATIVE_THREAD(me))
 
1603
        _PR_INTSOFF(is);
 
1604
    thread->state = _PR_RUNNABLE;
 
1605
    if ( !_PR_IS_NATIVE_THREAD(thread) ) {
 
1606
        _PR_THREAD_LOCK(thread);
 
1607
 
 
1608
        _PR_MISCQ_LOCK(thread->cpu);
 
1609
        _PR_DEL_JOINQ(thread);
 
1610
        _PR_MISCQ_UNLOCK(thread->cpu);
 
1611
 
 
1612
        _PR_AddThreadToRunQ(me, thread);
 
1613
        _PR_THREAD_UNLOCK(thread);
 
1614
    }
 
1615
    if (!_PR_IS_NATIVE_THREAD(me))
 
1616
        _PR_INTSON(is);
 
1617
 
 
1618
    _PR_MD_WAKEUP_WAITER(thread);
 
1619
 
 
1620
    return PR_SUCCESS;
 
1621
 
 
1622
ErrorExit:
 
1623
    if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is);
 
1624
    return PR_FAILURE;   
 
1625
}
 
1626
 
 
1627
PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread,
 
1628
    PRThreadPriority newPri)
 
1629
{
 
1630
 
 
1631
    /* 
 
1632
    First, pin down the priority.  Not all compilers catch passing out of
 
1633
    range enum here.  If we let bad values thru, priority queues won't work.
 
1634
    */
 
1635
    if ((PRIntn)newPri > (PRIntn)PR_PRIORITY_LAST) {
 
1636
        newPri = PR_PRIORITY_LAST;
 
1637
    } else if ((PRIntn)newPri < (PRIntn)PR_PRIORITY_FIRST) {
 
1638
        newPri = PR_PRIORITY_FIRST;
 
1639
    }
 
1640
        
 
1641
    if ( _PR_IS_NATIVE_THREAD(thread) ) {
 
1642
        thread->priority = newPri;
 
1643
        _PR_MD_SET_PRIORITY(&(thread->md), newPri);
 
1644
    } else _PR_SetThreadPriority(thread, newPri);
 
1645
}
 
1646
 
 
1647
 
 
1648
/*
 
1649
** This routine prevents all other threads from running. This call is needed by 
 
1650
** the garbage collector.
 
1651
*/
 
1652
PR_IMPLEMENT(void) PR_SuspendAll(void)
 
1653
{
 
1654
    PRThread *me = _PR_MD_CURRENT_THREAD();
 
1655
    PRCList *qp;
 
1656
 
 
1657
    /*
 
1658
     * Stop all user and native threads which are marked GC able.
 
1659
     */
 
1660
    PR_Lock(_pr_activeLock);
 
1661
    suspendAllOn = PR_TRUE;
 
1662
    suspendAllThread = _PR_MD_CURRENT_THREAD();
 
1663
    _PR_MD_BEGIN_SUSPEND_ALL();
 
1664
    for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
 
1665
        qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
 
1666
        if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && 
 
1667
            _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
 
1668
            _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp));
 
1669
                PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING);
 
1670
            }
 
1671
    }
 
1672
    for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
 
1673
        qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
 
1674
        if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
 
1675
            _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
 
1676
            /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */
 
1677
                _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); 
 
1678
    }
 
1679
    _PR_MD_END_SUSPEND_ALL();
 
1680
}
 
1681
 
 
1682
/*
 
1683
** This routine unblocks all other threads that were suspended from running by 
 
1684
** PR_SuspendAll(). This call is needed by the garbage collector.
 
1685
*/
 
1686
PR_IMPLEMENT(void) PR_ResumeAll(void)
 
1687
{
 
1688
    PRThread *me = _PR_MD_CURRENT_THREAD();
 
1689
    PRCList *qp;
 
1690
 
 
1691
    /*
 
1692
     * Resume all user and native threads which are marked GC able.
 
1693
     */
 
1694
    _PR_MD_BEGIN_RESUME_ALL();
 
1695
    for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
 
1696
        qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
 
1697
        if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && 
 
1698
            _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
 
1699
            _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp));
 
1700
    }
 
1701
    for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
 
1702
        qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
 
1703
        if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
 
1704
            _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
 
1705
                _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
 
1706
    }
 
1707
    _PR_MD_END_RESUME_ALL();
 
1708
    suspendAllThread = NULL;
 
1709
    suspendAllOn = PR_FALSE;
 
1710
    PR_Unlock(_pr_activeLock);
 
1711
}
 
1712
 
 
1713
PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
 
1714
{
 
1715
    PRCList *qp, *qp_next;
 
1716
    PRIntn i = 0;
 
1717
    PRStatus rv = PR_SUCCESS;
 
1718
    PRThread* t;
 
1719
 
 
1720
    /*
 
1721
    ** Currently Enumerate threads happen only with suspension and
 
1722
    ** pr_activeLock held
 
1723
    */
 
1724
    PR_ASSERT(suspendAllOn);
 
1725
 
 
1726
    /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
 
1727
     * qp->next after applying the function "func".  In particular, "func"
 
1728
     * might remove the thread from the queue and put it into another one in
 
1729
     * which case qp->next no longer points to the next entry in the original
 
1730
     * queue.
 
1731
     *
 
1732
     * To get around this problem, we save qp->next in qp_next before applying
 
1733
     * "func" and use that saved value as the next value after applying "func".
 
1734
     */
 
1735
 
 
1736
    /*
 
1737
     * Traverse the list of local and global threads
 
1738
     */
 
1739
    for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
 
1740
         qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next)
 
1741
    {
 
1742
        qp_next = qp->next;
 
1743
        t = _PR_ACTIVE_THREAD_PTR(qp);
 
1744
        if (_PR_IS_GCABLE_THREAD(t))
 
1745
        {
 
1746
            rv = (*func)(t, i, arg);
 
1747
            if (rv != PR_SUCCESS)
 
1748
                return rv;
 
1749
            i++;
 
1750
        }
 
1751
    }
 
1752
    for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
 
1753
         qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next)
 
1754
    {
 
1755
        qp_next = qp->next;
 
1756
        t = _PR_ACTIVE_THREAD_PTR(qp);
 
1757
        if (_PR_IS_GCABLE_THREAD(t))
 
1758
        {
 
1759
            rv = (*func)(t, i, arg);
 
1760
            if (rv != PR_SUCCESS)
 
1761
                return rv;
 
1762
            i++;
 
1763
        }
 
1764
    }
 
1765
    return rv;
 
1766
}
 
1767
 
 
1768
/* FUNCTION: _PR_AddSleepQ
 
1769
** DESCRIPTION:
 
1770
**    Adds a thread to the sleep/pauseQ.
 
1771
** RESTRICTIONS:
 
1772
**    Caller must have the RUNQ lock.
 
1773
**    Caller must be a user level thread
 
1774
*/
 
1775
PR_IMPLEMENT(void)
 
1776
_PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout)
 
1777
{
 
1778
    _PRCPU *cpu = thread->cpu;
 
1779
 
 
1780
    if (timeout == PR_INTERVAL_NO_TIMEOUT) {
 
1781
        /* append the thread to the global pause Q */
 
1782
        PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu));
 
1783
        thread->flags |= _PR_ON_PAUSEQ;
 
1784
    } else {
 
1785
        PRIntervalTime sleep;
 
1786
        PRCList *q;
 
1787
        PRThread *t;
 
1788
 
 
1789
        /* sort onto global sleepQ */
 
1790
        sleep = timeout;
 
1791
 
 
1792
        /* Check if we are longest timeout */
 
1793
        if (timeout >= _PR_SLEEPQMAX(cpu)) {
 
1794
            PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu));
 
1795
            thread->sleep = timeout - _PR_SLEEPQMAX(cpu);
 
1796
            _PR_SLEEPQMAX(cpu) = timeout;
 
1797
        } else {
 
1798
            /* Sort thread into global sleepQ at appropriate point */
 
1799
            q = _PR_SLEEPQ(cpu).next;
 
1800
 
 
1801
            /* Now scan the list for where to insert this entry */
 
1802
            while (q != &_PR_SLEEPQ(cpu)) {
 
1803
                t = _PR_THREAD_PTR(q);
 
1804
                if (sleep < t->sleep) {
 
1805
                    /* Found sleeper to insert in front of */
 
1806
                    break;
 
1807
                }
 
1808
                sleep -= t->sleep;
 
1809
                q = q->next;
 
1810
            }
 
1811
            thread->sleep = sleep;
 
1812
            PR_INSERT_BEFORE(&thread->links, q);
 
1813
 
 
1814
            /*
 
1815
            ** Subtract our sleep time from the sleeper that follows us (there
 
1816
            ** must be one) so that they remain relative to us.
 
1817
            */
 
1818
            PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu));
 
1819
          
 
1820
            t = _PR_THREAD_PTR(thread->links.next);
 
1821
            PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread);
 
1822
            t->sleep -= sleep;
 
1823
        }
 
1824
 
 
1825
        thread->flags |= _PR_ON_SLEEPQ;
 
1826
    }
 
1827
}
 
1828
 
 
1829
/* FUNCTION: _PR_DelSleepQ
 
1830
** DESCRIPTION:
 
1831
**    Removes a thread from the sleep/pauseQ.
 
1832
** INPUTS:
 
1833
**    If propogate_time is true, then the thread following the deleted
 
1834
**    thread will be get the time from the deleted thread.  This is used
 
1835
**    when deleting a sleeper that has not timed out.
 
1836
** RESTRICTIONS:
 
1837
**    Caller must have the RUNQ lock.
 
1838
**    Caller must be a user level thread
 
1839
*/
 
1840
PR_IMPLEMENT(void)
 
1841
_PR_DelSleepQ(PRThread *thread, PRBool propogate_time)
 
1842
{
 
1843
    _PRCPU *cpu = thread->cpu;
 
1844
 
 
1845
    /* Remove from pauseQ/sleepQ */
 
1846
    if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
 
1847
        if (thread->flags & _PR_ON_SLEEPQ) {
 
1848
            PRCList *q = thread->links.next;
 
1849
            if (q != &_PR_SLEEPQ(cpu)) {
 
1850
                if (propogate_time == PR_TRUE) {
 
1851
                    PRThread *after = _PR_THREAD_PTR(q);
 
1852
                    after->sleep += thread->sleep;
 
1853
                } else 
 
1854
                    _PR_SLEEPQMAX(cpu) -= thread->sleep;
 
1855
            } else {
 
1856
                /* Check if prev is the beggining of the list; if so,
 
1857
                 * we are the only element on the list.  
 
1858
                 */
 
1859
                if (thread->links.prev != &_PR_SLEEPQ(cpu))
 
1860
                    _PR_SLEEPQMAX(cpu) -= thread->sleep;
 
1861
                else
 
1862
                    _PR_SLEEPQMAX(cpu) = 0;
 
1863
            }
 
1864
            thread->flags &= ~_PR_ON_SLEEPQ;
 
1865
        } else {
 
1866
            thread->flags &= ~_PR_ON_PAUSEQ;
 
1867
        }
 
1868
        PR_REMOVE_LINK(&thread->links);
 
1869
    } else 
 
1870
        PR_ASSERT(0);
 
1871
}
 
1872
 
 
1873
void
 
1874
_PR_AddThreadToRunQ(
 
1875
    PRThread *me,     /* the current thread */
 
1876
    PRThread *thread) /* the local thread to be added to a run queue */
 
1877
{
 
1878
    PRThreadPriority pri = thread->priority;
 
1879
    _PRCPU *cpu = thread->cpu;
 
1880
 
 
1881
    PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
 
1882
 
 
1883
#if defined(WINNT)
 
1884
    /*
 
1885
     * On NT, we can only reliably know that the current CPU
 
1886
     * is not idle.  We add the awakened thread to the run
 
1887
     * queue of its CPU if its CPU is the current CPU.
 
1888
     * For any other CPU, we don't really know whether it
 
1889
     * is busy or idle.  So in all other cases, we just
 
1890
     * "post" the awakened thread to the IO completion port
 
1891
     * for the next idle CPU to execute (this is done in
 
1892
     * _PR_MD_WAKEUP_WAITER).
 
1893
         * Threads with a suspended I/O operation remain bound to
 
1894
         * the same cpu until I/O is cancelled
 
1895
     *
 
1896
     * NOTE: the boolean expression below must be the exact
 
1897
     * opposite of the corresponding boolean expression in
 
1898
     * _PR_MD_WAKEUP_WAITER.
 
1899
     */
 
1900
    if ((!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) ||
 
1901
                                        (thread->md.thr_bound_cpu)) {
 
1902
                PR_ASSERT(!thread->md.thr_bound_cpu ||
 
1903
                                                        (thread->md.thr_bound_cpu == cpu));
 
1904
        _PR_RUNQ_LOCK(cpu);
 
1905
        _PR_ADD_RUNQ(thread, cpu, pri);
 
1906
        _PR_RUNQ_UNLOCK(cpu);
 
1907
    }
 
1908
#else
 
1909
    _PR_RUNQ_LOCK(cpu);
 
1910
    _PR_ADD_RUNQ(thread, cpu, pri);
 
1911
    _PR_RUNQ_UNLOCK(cpu);
 
1912
    if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) {
 
1913
        if (pri > me->priority) {
 
1914
            _PR_SET_RESCHED_FLAG();
 
1915
        }
 
1916
    }
 
1917
#endif
 
1918
}