Coverage Report

Created: 2026-02-05 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nspr/pr/src/pthreads/ptthread.c
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
/*
7
** File:            ptthread.c
8
** Descritpion:        Implemenation for threds using pthreds
9
** Exports:            ptthread.h
10
*/
11
12
#if defined(_PR_PTHREADS)
13
14
#  include "prlog.h"
15
#  include "primpl.h"
16
#  include "prpdce.h"
17
18
#  include <pthread.h>
19
#  include <unistd.h>
20
#  include <string.h>
21
#  include <signal.h>
22
#  include <dlfcn.h>
23
24
#  if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)
25
#    include <pthread_np.h>
26
#  endif
27
28
#  if defined(ANDROID)
29
#    include <sys/prctl.h>
30
#  endif
31
32
#  ifdef _PR_NICE_PRIORITY_SCHEDULING
33
#    undef _POSIX_THREAD_PRIORITY_SCHEDULING
34
#    include <sys/resource.h>
35
#    ifndef HAVE_GETTID
36
#      define gettid() (syscall(SYS_gettid))
37
#    endif
38
#  endif
39
40
/*
41
 * Record whether or not we have the privilege to set the scheduling
42
 * policy and priority of threads.  0 means that privilege is available.
43
 * EPERM means that privilege is not available.
44
 */
45
46
static PRIntn pt_schedpriv = 0;
47
extern PRLock* _pr_sleeplock;
48
49
static struct _PT_Bookeeping {
50
  PRLock* ml;             /* a lock to protect ourselves */
51
  PRCondVar* cv;          /* used to signal global things */
52
  PRInt32 system, user;   /* a count of the two different types */
53
  PRUintn this_many;      /* number of threads allowed for exit */
54
  pthread_key_t key;      /* thread private data key */
55
  PRBool keyCreated;      /* whether 'key' should be deleted */
56
  PRThread *first, *last; /* list of threads we know about */
57
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
58
  PRInt32 minPrio, maxPrio; /* range of scheduling priorities */
59
#  endif
60
} pt_book = {0};
61
62
static void _pt_thread_death(void* arg);
63
static void _pt_thread_death_internal(void* arg, PRBool callDestructors);
64
static void init_pthread_gc_support(void);
65
66
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
67
static PRIntn pt_PriorityMap(PRThreadPriority pri) {
68
#    ifdef NTO
69
  /* This priority algorithm causes lots of problems on Neutrino
70
   * for now I have just hard coded everything to run at priority 10
71
   * until I can come up with a new algorithm.
72
   *     Jerry.Kirk@Nexwarecorp.com
73
   */
74
  return 10;
75
#    else
76
  return pt_book.minPrio +
77
         pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
78
#    endif
79
}
80
#  elif defined(_PR_NICE_PRIORITY_SCHEDULING)
81
/*
82
 * This functions maps higher priorities to lower nice values relative to the
83
 * nice value specified in the |nice| parameter. The corresponding relative
84
 * adjustments are:
85
 *
86
 * PR_PRIORITY_LOW    +1
87
 * PR_PRIORITY_NORMAL  0
88
 * PR_PRIORITY_HIGH   -1
89
 * PR_PRIORITY_URGENT -2
90
 */
91
0
static int pt_RelativePriority(int nice, PRThreadPriority pri) {
92
0
  return nice + (1 - pri);
93
0
}
94
#  endif
95
96
/*
97
** Initialize a stack for a native pthread thread
98
*/
99
19
static void _PR_InitializeStack(PRThreadStack* ts) {
100
19
  if (ts && (ts->stackTop == 0)) {
101
19
    ts->allocBase = (char*)&ts;
102
19
    ts->allocSize = ts->stackSize;
103
104
    /*
105
    ** Setup stackTop and stackBottom values.
106
    */
107
#  ifdef HAVE_STACK_GROWING_UP
108
    ts->stackBottom = ts->allocBase + ts->stackSize;
109
    ts->stackTop = ts->allocBase;
110
#  else
111
19
    ts->stackTop = ts->allocBase;
112
19
    ts->stackBottom = ts->allocBase - ts->stackSize;
113
19
#  endif
114
19
  }
115
19
}
116
117
0
static void* _pt_root(void* arg) {
118
0
  PRIntn rv;
119
0
  PRThread* thred = (PRThread*)arg;
120
0
  PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE;
121
0
  pthread_t id = pthread_self();
122
0
#  ifdef _PR_NICE_PRIORITY_SCHEDULING
123
0
  pid_t tid;
124
0
#  endif
125
126
0
#  ifdef _PR_NICE_PRIORITY_SCHEDULING
127
  /*
128
   * We need to know the kernel thread ID of each thread in order to
129
   * set its nice value hence we do it here instead of at creation time.
130
   */
131
0
  tid = gettid();
132
0
  errno = 0;
133
0
  rv = getpriority(PRIO_PROCESS, 0);
134
135
  /* If we cannot read the main thread's nice value don't try to change the
136
   * new thread's nice value. */
137
0
  if (errno == 0) {
138
0
    setpriority(PRIO_PROCESS, tid, pt_RelativePriority(rv, thred->priority));
139
0
  }
140
0
#  endif
141
142
  /* Set up the thread stack information */
143
0
  _PR_InitializeStack(thred->stack);
144
145
  /*
146
   * Set within the current thread the pointer to our object.
147
   * This object will be deleted when the thread termintates,
148
   * whether in a join or detached (see _PR_InitThreads()).
149
   */
150
0
  rv = pthread_setspecific(pt_book.key, thred);
151
0
  PR_ASSERT(0 == rv);
152
153
  /* make the thread visible to the rest of the runtime */
154
0
  PR_Lock(pt_book.ml);
155
  /*
156
   * Both the parent thread and this new thread set thred->id.
157
   * The new thread must ensure that thred->id is set before
158
   * it executes its startFunc.  The parent thread must ensure
159
   * that thred->id is set before PR_CreateThread() returns.
160
   * Both threads set thred->id while holding pt_book.ml and
161
   * use thred->idSet to ensure thred->id is written only once.
162
   */
163
0
  if (!thred->idSet) {
164
0
    thred->id = id;
165
0
    thred->idSet = PR_TRUE;
166
0
  } else {
167
0
    PR_ASSERT(pthread_equal(thred->id, id));
168
0
  }
169
170
0
#  ifdef _PR_NICE_PRIORITY_SCHEDULING
171
0
  thred->tid = tid;
172
0
  PR_NotifyAllCondVar(pt_book.cv);
173
0
#  endif
174
175
  /* If this is a GCABLE thread, set its state appropriately */
176
0
  if (thred->suspend & PT_THREAD_SETGCABLE) {
177
0
    thred->state |= PT_THREAD_GCABLE;
178
0
  }
179
0
  thred->suspend = 0;
180
181
0
  thred->prev = pt_book.last;
182
0
  if (pt_book.last) {
183
0
    pt_book.last->next = thred;
184
0
  } else {
185
0
    pt_book.first = thred;
186
0
  }
187
0
  thred->next = NULL;
188
0
  pt_book.last = thred;
189
0
  PR_Unlock(pt_book.ml);
190
191
0
  thred->startFunc(thred->arg); /* make visible to the client */
192
193
  /* unhook the thread from the runtime */
194
0
  PR_Lock(pt_book.ml);
195
  /*
196
   * At this moment, PR_CreateThread() may not have set thred->id yet.
197
   * It is safe for a detached thread to free thred only after
198
   * PR_CreateThread() has accessed thred->id and thred->idSet.
199
   */
200
0
  if (detached) {
201
0
    while (!thred->okToDelete) {
202
0
      PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
203
0
    }
204
0
  }
205
206
0
  if (thred->state & PT_THREAD_SYSTEM) {
207
0
    pt_book.system -= 1;
208
0
  } else if (--pt_book.user == pt_book.this_many) {
209
0
    PR_NotifyAllCondVar(pt_book.cv);
210
0
  }
211
0
  if (NULL == thred->prev) {
212
0
    pt_book.first = thred->next;
213
0
  } else {
214
0
    thred->prev->next = thred->next;
215
0
  }
216
0
  if (NULL == thred->next) {
217
0
    pt_book.last = thred->prev;
218
0
  } else {
219
0
    thred->next->prev = thred->prev;
220
0
  }
221
0
  PR_Unlock(pt_book.ml);
222
223
  /*
224
   * Here we set the pthread's backpointer to the PRThread to NULL.
225
   * Otherwise the destructor would get called eagerly as the thread
226
   * returns to the pthread runtime. The joining thread would them be
227
   * the proud possessor of a dangling reference. However, this is the
228
   * last chance to delete the object if the thread is detached, so
229
   * just let the destructor do the work.
230
   */
231
0
  if (PR_FALSE == detached) {
232
    /* Call TPD destructors on this thread. */
233
0
    _PR_DestroyThreadPrivate(thred);
234
0
    rv = pthread_setspecific(pt_book.key, NULL);
235
0
    PR_ASSERT(0 == rv);
236
0
  }
237
238
0
  return NULL;
239
0
} /* _pt_root */
240
241
0
static PRThread* pt_AttachThread(void) {
242
0
  PRThread* thred = NULL;
243
244
  /*
245
   * NSPR must have been initialized when PR_AttachThread is called.
246
   * We cannot have PR_AttachThread call implicit initialization
247
   * because if multiple threads call PR_AttachThread simultaneously,
248
   * NSPR may be initialized more than once.
249
   * We can't call any function that calls PR_GetCurrentThread()
250
   * either (e.g., PR_SetError()) as that will result in infinite
251
   * recursion.
252
   */
253
0
  if (!_pr_initialized) {
254
0
    return NULL;
255
0
  }
256
257
  /* PR_NEWZAP must not call PR_GetCurrentThread() */
258
0
  thred = PR_NEWZAP(PRThread);
259
0
  if (NULL != thred) {
260
0
    int rv;
261
262
0
    thred->priority = PR_PRIORITY_NORMAL;
263
0
    thred->id = pthread_self();
264
0
    thred->idSet = PR_TRUE;
265
0
#  ifdef _PR_NICE_PRIORITY_SCHEDULING
266
0
    thred->tid = gettid();
267
0
#  endif
268
0
    rv = pthread_setspecific(pt_book.key, thred);
269
0
    PR_ASSERT(0 == rv);
270
271
0
    thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN;
272
0
    PR_Lock(pt_book.ml);
273
274
    /* then put it into the list */
275
0
    thred->prev = pt_book.last;
276
0
    if (pt_book.last) {
277
0
      pt_book.last->next = thred;
278
0
    } else {
279
0
      pt_book.first = thred;
280
0
    }
281
0
    thred->next = NULL;
282
0
    pt_book.last = thred;
283
0
    PR_Unlock(pt_book.ml);
284
0
  }
285
0
  return thred; /* may be NULL */
286
0
} /* pt_AttachThread */
287
288
static PRThread* _PR_CreateThread(PRThreadType type, void (*start)(void* arg),
289
                                  void* arg, PRThreadPriority priority,
290
                                  PRThreadScope scope, PRThreadState state,
291
0
                                  PRUint32 stackSize, PRBool isGCAble) {
292
0
  int rv;
293
0
  PRThread* thred;
294
0
  pthread_attr_t tattr;
295
296
0
  if (!_pr_initialized) {
297
0
    _PR_ImplicitInitialization();
298
0
  }
299
300
0
  if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) {
301
0
    priority = PR_PRIORITY_FIRST;
302
0
  } else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) {
303
0
    priority = PR_PRIORITY_LAST;
304
0
  }
305
306
0
  rv = _PT_PTHREAD_ATTR_INIT(&tattr);
307
0
  PR_ASSERT(0 == rv);
308
309
0
  if (EPERM != pt_schedpriv) {
310
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
311
    struct sched_param schedule;
312
#  endif
313
314
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
315
    rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
316
    PR_ASSERT(0 == rv);
317
#  endif
318
319
    /* Use the default scheduling policy */
320
321
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
322
    rv = pthread_attr_getschedparam(&tattr, &schedule);
323
    PR_ASSERT(0 == rv);
324
    schedule.sched_priority = pt_PriorityMap(priority);
325
    rv = pthread_attr_setschedparam(&tattr, &schedule);
326
    PR_ASSERT(0 == rv);
327
#    ifdef NTO
328
    rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */
329
    PR_ASSERT(0 == rv);
330
#    endif
331
#  endif /* _POSIX_THREAD_PRIORITY_SCHEDULING > 0 */
332
0
  }
333
334
0
  rv = pthread_attr_setdetachstate(
335
0
      &tattr, ((PR_JOINABLE_THREAD == state) ? PTHREAD_CREATE_JOINABLE
336
0
                                             : PTHREAD_CREATE_DETACHED));
337
0
  PR_ASSERT(0 == rv);
338
339
  /*
340
   * If stackSize is 0, we use the default pthread stack size.
341
   */
342
0
  if (stackSize) {
343
#  ifdef _MD_MINIMUM_STACK_SIZE
344
    if (stackSize < _MD_MINIMUM_STACK_SIZE) {
345
      stackSize = _MD_MINIMUM_STACK_SIZE;
346
    }
347
#  endif
348
0
    rv = pthread_attr_setstacksize(&tattr, stackSize);
349
0
    PR_ASSERT(0 == rv);
350
0
  }
351
352
0
  thred = PR_NEWZAP(PRThread);
353
0
  if (NULL == thred) {
354
0
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno);
355
0
    goto done;
356
0
  } else {
357
0
    pthread_t id;
358
359
0
    thred->arg = arg;
360
0
    thred->startFunc = start;
361
0
    thred->priority = priority;
362
0
    if (PR_UNJOINABLE_THREAD == state) {
363
0
      thred->state |= PT_THREAD_DETACHED;
364
0
    }
365
366
0
    if (PR_LOCAL_THREAD == scope) {
367
0
      scope = PR_GLOBAL_THREAD;
368
0
    }
369
370
0
    if (PR_GLOBAL_BOUND_THREAD == scope) {
371
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
372
      rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
373
      if (rv) {
374
        /*
375
         * system scope not supported
376
         */
377
        scope = PR_GLOBAL_THREAD;
378
        /*
379
         * reset scope
380
         */
381
        rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
382
        PR_ASSERT(0 == rv);
383
      }
384
#  endif
385
0
    }
386
0
    if (PR_GLOBAL_THREAD == scope) {
387
0
      thred->state |= PT_THREAD_GLOBAL;
388
0
    } else if (PR_GLOBAL_BOUND_THREAD == scope) {
389
0
      thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND);
390
0
    } else { /* force it global */
391
0
      thred->state |= PT_THREAD_GLOBAL;
392
0
    }
393
0
    if (PR_SYSTEM_THREAD == type) {
394
0
      thred->state |= PT_THREAD_SYSTEM;
395
0
    }
396
397
0
    thred->suspend = (isGCAble) ? PT_THREAD_SETGCABLE : 0;
398
399
0
    thred->stack = PR_NEWZAP(PRThreadStack);
400
0
    if (thred->stack == NULL) {
401
0
      PRIntn oserr = errno;
402
0
      PR_Free(thred); /* all that work ... poof! */
403
0
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr);
404
0
      thred = NULL; /* and for what? */
405
0
      goto done;
406
0
    }
407
0
    thred->stack->stackSize = stackSize;
408
0
    thred->stack->thr = thred;
409
410
0
#  ifdef PT_NO_SIGTIMEDWAIT
411
0
    pthread_mutex_init(&thred->suspendResumeMutex, NULL);
412
0
    pthread_cond_init(&thred->suspendResumeCV, NULL);
413
0
#  endif
414
415
    /* make the thread counted to the rest of the runtime */
416
0
    PR_Lock(pt_book.ml);
417
0
    if (PR_SYSTEM_THREAD == type) {
418
0
      pt_book.system += 1;
419
0
    } else {
420
0
      pt_book.user += 1;
421
0
    }
422
0
    PR_Unlock(pt_book.ml);
423
424
    /*
425
     * We pass a pointer to a local copy (instead of thred->id)
426
     * to pthread_create() because who knows what wacky things
427
     * pthread_create() may be doing to its argument.
428
     */
429
0
    rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
430
431
0
    if (EPERM == rv) {
432
      /* Remember that we don't have thread scheduling privilege. */
433
0
      pt_schedpriv = EPERM;
434
0
      PR_LOG(_pr_thread_lm, PR_LOG_MIN,
435
0
             ("_PR_CreateThread: no thread scheduling privilege"));
436
      /* Try creating the thread again without setting priority. */
437
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
438
      rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED);
439
      PR_ASSERT(0 == rv);
440
#  endif
441
0
      rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
442
0
    }
443
444
0
    if (0 != rv) {
445
0
      PRIntn oserr = rv;
446
0
      PR_Lock(pt_book.ml);
447
0
      if (thred->state & PT_THREAD_SYSTEM) {
448
0
        pt_book.system -= 1;
449
0
      } else if (--pt_book.user == pt_book.this_many) {
450
0
        PR_NotifyAllCondVar(pt_book.cv);
451
0
      }
452
0
      PR_Unlock(pt_book.ml);
453
454
0
      PR_Free(thred->stack);
455
0
      PR_Free(thred); /* all that work ... poof! */
456
0
      PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr);
457
0
      thred = NULL; /* and for what? */
458
0
      goto done;
459
0
    }
460
461
0
    PR_Lock(pt_book.ml);
462
    /*
463
     * Both the parent thread and this new thread set thred->id.
464
     * The parent thread must ensure that thred->id is set before
465
     * PR_CreateThread() returns.  (See comments in _pt_root().)
466
     */
467
0
    if (!thred->idSet) {
468
0
      thred->id = id;
469
0
      thred->idSet = PR_TRUE;
470
0
    } else {
471
0
      PR_ASSERT(pthread_equal(thred->id, id));
472
0
    }
473
474
    /*
475
     * If the new thread is detached, tell it that PR_CreateThread() has
476
     * accessed thred->id and thred->idSet so it's ok to delete thred.
477
     */
478
0
    if (PR_UNJOINABLE_THREAD == state) {
479
0
      thred->okToDelete = PR_TRUE;
480
0
      PR_NotifyAllCondVar(pt_book.cv);
481
0
    }
482
0
    PR_Unlock(pt_book.ml);
483
0
  }
484
485
0
done:
486
0
  rv = _PT_PTHREAD_ATTR_DESTROY(&tattr);
487
0
  PR_ASSERT(0 == rv);
488
489
0
  return thred;
490
0
} /* _PR_CreateThread */
491
492
PR_IMPLEMENT(PRThread*)
493
PR_CreateThread(PRThreadType type, void (*start)(void* arg), void* arg,
494
                PRThreadPriority priority, PRThreadScope scope,
495
0
                PRThreadState state, PRUint32 stackSize) {
496
0
  return _PR_CreateThread(type, start, arg, priority, scope, state, stackSize,
497
0
                          PR_FALSE);
498
0
} /* PR_CreateThread */
499
500
PR_IMPLEMENT(PRThread*)
501
PR_CreateThreadGCAble(PRThreadType type, void (*start)(void* arg), void* arg,
502
                      PRThreadPriority priority, PRThreadScope scope,
503
0
                      PRThreadState state, PRUint32 stackSize) {
504
0
  return _PR_CreateThread(type, start, arg, priority, scope, state, stackSize,
505
0
                          PR_TRUE);
506
0
} /* PR_CreateThreadGCAble */
507
508
0
PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread* thred) {
509
0
  return thred->environment;
510
0
} /* GetExecutionEnvironment */
511
512
0
PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread* thred, void* env) {
513
0
  thred->environment = env;
514
0
} /* SetExecutionEnvironment */
515
516
PR_IMPLEMENT(PRThread*)
517
PR_AttachThread(PRThreadType type, PRThreadPriority priority,
518
0
                PRThreadStack* stack) {
519
0
  return PR_GetCurrentThread();
520
0
} /* PR_AttachThread */
521
522
0
PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread* thred) {
523
0
  int rv = -1;
524
0
  void* result = NULL;
525
0
  PR_ASSERT(thred != NULL);
526
527
0
  if ((0xafafafaf == thred->state) ||
528
0
      (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state)) ||
529
0
      (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state))) {
530
    /*
531
     * This might be a bad address, but if it isn't, the state should
532
     * either be an unjoinable thread or it's already had the object
533
     * deleted. However, the client that called join on a detached
534
     * thread deserves all the rath I can muster....
535
     */
536
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
537
0
    PR_LogPrint("PR_JoinThread: %p not joinable | already smashed\n", thred);
538
0
  } else {
539
0
    pthread_t id = thred->id;
540
0
    rv = pthread_join(id, &result);
541
0
    PR_ASSERT(rv == 0 && result == NULL);
542
0
    if (0 == rv) {
543
      /*
544
       * PR_FALSE, because the thread already called the TPD
545
       * destructors before exiting _pt_root.
546
       */
547
0
      _pt_thread_death_internal(thred, PR_FALSE);
548
0
    } else {
549
0
      PRErrorCode prerror;
550
0
      switch (rv) {
551
0
        case EINVAL: /* not a joinable thread */
552
0
        case ESRCH:  /* no thread with given ID */
553
0
          prerror = PR_INVALID_ARGUMENT_ERROR;
554
0
          break;
555
0
        case EDEADLK: /* a thread joining with itself */
556
0
          prerror = PR_DEADLOCK_ERROR;
557
0
          break;
558
0
        default:
559
0
          prerror = PR_UNKNOWN_ERROR;
560
0
          break;
561
0
      }
562
0
      PR_SetError(prerror, rv);
563
0
    }
564
0
  }
565
0
  return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
566
0
} /* PR_JoinThread */
567
568
0
PR_IMPLEMENT(void) PR_DetachThread(void) {
569
0
  void* thred;
570
0
  int rv;
571
572
0
  _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
573
0
  if (NULL == thred) {
574
0
    return;
575
0
  }
576
0
  _pt_thread_death(thred);
577
0
  rv = pthread_setspecific(pt_book.key, NULL);
578
0
  PR_ASSERT(0 == rv);
579
0
} /* PR_DetachThread */
580
581
278M
PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void) {
582
278M
  void* thred;
583
584
278M
  if (!_pr_initialized) {
585
1
    _PR_ImplicitInitialization();
586
1
  }
587
588
278M
  _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
589
278M
  if (NULL == thred) {
590
0
    thred = pt_AttachThread();
591
0
  }
592
278M
  PR_ASSERT(NULL != thred);
593
278M
  return (PRThread*)thred;
594
278M
} /* PR_GetCurrentThread */
595
596
0
PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread* thred) {
597
0
  return (thred->state & PT_THREAD_BOUND) ? PR_GLOBAL_BOUND_THREAD
598
0
                                          : PR_GLOBAL_THREAD;
599
0
} /* PR_GetThreadScope() */
600
601
0
PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread* thred) {
602
0
  return (thred->state & PT_THREAD_SYSTEM) ? PR_SYSTEM_THREAD : PR_USER_THREAD;
603
0
}
604
605
0
PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread* thred) {
606
0
  return (thred->state & PT_THREAD_DETACHED) ? PR_UNJOINABLE_THREAD
607
0
                                             : PR_JOINABLE_THREAD;
608
0
} /* PR_GetThreadState */
609
610
0
PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread* thred) {
611
0
  PR_ASSERT(thred != NULL);
612
0
  return thred->priority;
613
0
} /* PR_GetThreadPriority */
614
615
PR_IMPLEMENT(void)
616
0
PR_SetThreadPriority(PRThread* thred, PRThreadPriority newPri) {
617
0
  PRIntn rv;
618
619
0
  PR_ASSERT(NULL != thred);
620
621
0
  if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri) {
622
0
    newPri = PR_PRIORITY_FIRST;
623
0
  } else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri) {
624
0
    newPri = PR_PRIORITY_LAST;
625
0
  }
626
627
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
628
  if (EPERM != pt_schedpriv) {
629
    int policy;
630
    struct sched_param schedule;
631
632
    rv = pthread_getschedparam(thred->id, &policy, &schedule);
633
    if (0 == rv) {
634
      schedule.sched_priority = pt_PriorityMap(newPri);
635
      rv = pthread_setschedparam(thred->id, policy, &schedule);
636
      if (EPERM == rv) {
637
        pt_schedpriv = EPERM;
638
        PR_LOG(_pr_thread_lm, PR_LOG_MIN,
639
               ("PR_SetThreadPriority: no thread scheduling privilege"));
640
      }
641
    }
642
    if (rv != 0) {
643
      rv = -1;
644
    }
645
  }
646
#  elif defined(_PR_NICE_PRIORITY_SCHEDULING)
647
  PR_Lock(pt_book.ml);
648
0
  while (thred->tid == 0) {
649
0
    PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
650
0
  }
651
0
  PR_Unlock(pt_book.ml);
652
653
0
  errno = 0;
654
0
  rv = getpriority(PRIO_PROCESS, 0);
655
656
  /* Do not proceed unless we know the main thread's nice value. */
657
0
  if (errno == 0) {
658
0
    rv = setpriority(PRIO_PROCESS, thred->tid, pt_RelativePriority(rv, newPri));
659
660
0
    if (rv == -1) {
661
      /* We don't set pt_schedpriv to EPERM in case errno == EPERM
662
       * because adjusting the nice value might be permitted for certain
663
       * ranges but not for others. */
664
0
      PR_LOG(_pr_thread_lm, PR_LOG_MIN,
665
0
             ("PR_SetThreadPriority: setpriority failed with error %d", errno));
666
0
    }
667
0
  }
668
#  else
669
  (void)rv; /* rv is unused */
670
#  endif
671
672
0
  thred->priority = newPri;
673
0
} /* PR_SetThreadPriority */
674
675
0
PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread* thred) {
676
  /*
677
  ** If the target thread indicates that it's waiting,
678
  ** find the condition and broadcast to it. Broadcast
679
  ** since we don't know which thread (if there are more
680
  ** than one). This sounds risky, but clients must
681
  ** test their invariants when resumed from a wait and
682
  ** I don't expect very many threads to be waiting on
683
  ** a single condition and I don't expect interrupt to
684
  ** be used very often.
685
  **
686
  ** I don't know why I thought this would work. Must have
687
  ** been one of those weaker momements after I'd been
688
  ** smelling the vapors.
689
  **
690
  ** Even with the followng changes it is possible that
691
  ** the pointer to the condition variable is pointing
692
  ** at a bogus value. Will the unerlying code detect
693
  ** that?
694
  */
695
0
  PRCondVar* cv;
696
0
  PR_ASSERT(NULL != thred);
697
0
  if (NULL == thred) {
698
0
    return PR_FAILURE;
699
0
  }
700
701
0
  thred->state |= PT_THREAD_ABORTED;
702
703
0
  cv = thred->waiting;
704
0
  if ((NULL != cv) && !thred->interrupt_blocked) {
705
0
    PRIntn rv;
706
0
    (void)PR_ATOMIC_INCREMENT(&cv->notify_pending);
707
0
    rv = pthread_cond_broadcast(&cv->cv);
708
0
    PR_ASSERT(0 == rv);
709
0
    if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) {
710
0
      PR_DestroyCondVar(cv);
711
0
    }
712
0
  }
713
0
  return PR_SUCCESS;
714
0
} /* PR_Interrupt */
715
716
0
PR_IMPLEMENT(void) PR_ClearInterrupt(void) {
717
0
  PRThread* me = PR_GetCurrentThread();
718
0
  me->state &= ~PT_THREAD_ABORTED;
719
0
} /* PR_ClearInterrupt */
720
721
0
PR_IMPLEMENT(void) PR_BlockInterrupt(void) {
722
0
  PRThread* me = PR_GetCurrentThread();
723
0
  _PT_THREAD_BLOCK_INTERRUPT(me);
724
0
} /* PR_BlockInterrupt */
725
726
0
PR_IMPLEMENT(void) PR_UnblockInterrupt(void) {
727
0
  PRThread* me = PR_GetCurrentThread();
728
0
  _PT_THREAD_UNBLOCK_INTERRUPT(me);
729
0
} /* PR_UnblockInterrupt */
730
731
0
PR_IMPLEMENT(PRStatus) PR_Yield(void) {
732
0
  static PRBool warning = PR_TRUE;
733
0
  if (warning)
734
0
    warning = _PR_Obsolete("PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
735
0
  return PR_Sleep(PR_INTERVAL_NO_WAIT);
736
0
}
737
738
2.44k
PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks) {
739
2.44k
  PRStatus rv = PR_SUCCESS;
740
741
2.44k
  if (!_pr_initialized) {
742
0
    _PR_ImplicitInitialization();
743
0
  }
744
745
2.44k
  if (PR_INTERVAL_NO_WAIT == ticks) {
746
2.44k
    _PT_PTHREAD_YIELD();
747
2.44k
  } else {
748
0
    PRCondVar* cv;
749
0
    PRIntervalTime timein;
750
751
0
    timein = PR_IntervalNow();
752
0
    cv = PR_NewCondVar(_pr_sleeplock);
753
0
    PR_ASSERT(cv != NULL);
754
0
    PR_Lock(_pr_sleeplock);
755
0
    do {
756
0
      PRIntervalTime now = PR_IntervalNow();
757
0
      PRIntervalTime delta = now - timein;
758
0
      if (delta > ticks) {
759
0
        break;
760
0
      }
761
0
      rv = PR_WaitCondVar(cv, ticks - delta);
762
0
    } while (PR_SUCCESS == rv);
763
0
    PR_Unlock(_pr_sleeplock);
764
0
    PR_DestroyCondVar(cv);
765
0
  }
766
2.44k
  return rv;
767
2.44k
} /* PR_Sleep */
768
769
0
static void _pt_thread_death(void* arg) {
770
0
  void* thred;
771
0
  int rv;
772
773
0
  _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
774
0
  if (NULL == thred) {
775
    /*
776
     * Have PR_GetCurrentThread return the expected value to the
777
     * destructors.
778
     */
779
0
    rv = pthread_setspecific(pt_book.key, arg);
780
0
    PR_ASSERT(0 == rv);
781
0
  }
782
783
  /* PR_TRUE for: call destructors */
784
0
  _pt_thread_death_internal(arg, PR_TRUE);
785
786
0
  if (NULL == thred) {
787
0
    rv = pthread_setspecific(pt_book.key, NULL);
788
0
    PR_ASSERT(0 == rv);
789
0
  }
790
0
}
791
792
0
static void _pt_thread_death_internal(void* arg, PRBool callDestructors) {
793
0
  PRThread* thred = (PRThread*)arg;
794
795
0
  if (thred->state & (PT_THREAD_FOREIGN | PT_THREAD_PRIMORD)) {
796
0
    PR_Lock(pt_book.ml);
797
0
    if (NULL == thred->prev) {
798
0
      pt_book.first = thred->next;
799
0
    } else {
800
0
      thred->prev->next = thred->next;
801
0
    }
802
0
    if (NULL == thred->next) {
803
0
      pt_book.last = thred->prev;
804
0
    } else {
805
0
      thred->next->prev = thred->prev;
806
0
    }
807
0
    PR_Unlock(pt_book.ml);
808
0
  }
809
0
  if (callDestructors) {
810
0
    _PR_DestroyThreadPrivate(thred);
811
0
  }
812
0
  PR_Free(thred->privateData);
813
0
  if (NULL != thred->errorString) {
814
0
    PR_Free(thred->errorString);
815
0
  }
816
0
  if (NULL != thred->name) {
817
0
    PR_Free(thred->name);
818
0
  }
819
0
  PR_Free(thred->stack);
820
0
  if (NULL != thred->syspoll_list) {
821
0
    PR_Free(thred->syspoll_list);
822
0
  }
823
0
#  if defined(DEBUG)
824
0
  memset(thred, 0xaf, sizeof(PRThread));
825
0
#  endif /* defined(DEBUG) */
826
0
  PR_Free(thred);
827
0
} /* _pt_thread_death */
828
829
void _PR_InitThreads(PRThreadType type, PRThreadPriority priority,
830
19
                     PRUintn maxPTDs) {
831
19
  int rv;
832
19
  PRThread* thred;
833
834
19
  PR_ASSERT(priority == PR_PRIORITY_NORMAL);
835
836
#  ifdef _PR_NEED_PTHREAD_INIT
837
  /*
838
   * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily
839
   * initialized, but pthread_self() fails to initialize
840
   * pthreads and hence returns a null thread ID if invoked
841
   * by the primordial thread before any other pthread call.
842
   * So we explicitly initialize pthreads here.
843
   */
844
  pthread_init();
845
#  endif
846
847
#  if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
848
#    if defined(FREEBSD)
849
  {
850
    pthread_attr_t attr;
851
    int policy;
852
    /* get the min and max priorities of the default policy */
853
    pthread_attr_init(&attr);
854
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
855
    pthread_attr_getschedpolicy(&attr, &policy);
856
    pt_book.minPrio = sched_get_priority_min(policy);
857
    PR_ASSERT(-1 != pt_book.minPrio);
858
    pt_book.maxPrio = sched_get_priority_max(policy);
859
    PR_ASSERT(-1 != pt_book.maxPrio);
860
    pthread_attr_destroy(&attr);
861
  }
862
#    else
863
  /*
864
  ** These might be function evaluations
865
  */
866
  pt_book.minPrio = PT_PRIO_MIN;
867
  pt_book.maxPrio = PT_PRIO_MAX;
868
#    endif
869
#  endif
870
871
19
  PR_ASSERT(NULL == pt_book.ml);
872
19
  pt_book.ml = PR_NewLock();
873
19
  PR_ASSERT(NULL != pt_book.ml);
874
19
  pt_book.cv = PR_NewCondVar(pt_book.ml);
875
19
  PR_ASSERT(NULL != pt_book.cv);
876
19
  thred = PR_NEWZAP(PRThread);
877
19
  PR_ASSERT(NULL != thred);
878
19
  thred->arg = NULL;
879
19
  thred->startFunc = NULL;
880
19
  thred->priority = priority;
881
19
  thred->id = pthread_self();
882
19
  thred->idSet = PR_TRUE;
883
19
#  ifdef _PR_NICE_PRIORITY_SCHEDULING
884
19
  thred->tid = gettid();
885
19
#  endif
886
887
19
  thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD);
888
19
  if (PR_SYSTEM_THREAD == type) {
889
0
    thred->state |= PT_THREAD_SYSTEM;
890
0
    pt_book.system += 1;
891
0
    pt_book.this_many = 0;
892
19
  } else {
893
19
    pt_book.user += 1;
894
19
    pt_book.this_many = 1;
895
19
  }
896
19
  thred->next = thred->prev = NULL;
897
19
  pt_book.first = pt_book.last = thred;
898
899
19
  thred->stack = PR_NEWZAP(PRThreadStack);
900
19
  PR_ASSERT(thred->stack != NULL);
901
19
  thred->stack->stackSize = 0;
902
19
  thred->stack->thr = thred;
903
19
  _PR_InitializeStack(thred->stack);
904
905
  /*
906
   * Create a key for our use to store a backpointer in the pthread
907
   * to our PRThread object. This object gets deleted when the thread
908
   * returns from its root in the case of a detached thread. Other
909
   * threads delete the objects in Join.
910
   *
911
   * NB: The destructor logic seems to have a bug so it isn't used.
912
   * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998.
913
   * More info - the problem is that pthreads calls the destructor
914
   * eagerly as the thread returns from its root, rather than lazily
915
   * after the thread is joined. Therefore, threads that are joining
916
   * and holding PRThread references are actually holding pointers to
917
   * nothing.
918
   */
919
19
  rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death);
920
19
  if (0 != rv) {
921
0
    PR_Assert("0 == rv", __FILE__, __LINE__);
922
0
  }
923
19
  pt_book.keyCreated = PR_TRUE;
924
19
  rv = pthread_setspecific(pt_book.key, thred);
925
19
  PR_ASSERT(0 == rv);
926
19
} /* _PR_InitThreads */
927
928
#  ifdef __GNUC__
929
/*
930
 * GCC supports the constructor and destructor attributes as of
931
 * version 2.5.
932
 */
933
#    if defined(DARWIN)
934
/*
935
 * The dynamic linker on OSX doesn't execute __attribute__((destructor))
936
 * destructors in the right order wrt non-__attribute((destructor)) destructors
937
 * in other libraries. So use atexit() instead, which does.
938
 * See https://bugzilla.mozilla.org/show_bug.cgi?id=1399746#c99
939
 */
940
static void _PR_Fini(void);
941
942
__attribute__((constructor)) static void _register_PR_Fini() {
943
  atexit(_PR_Fini);
944
}
945
#    else
946
static void _PR_Fini(void) __attribute__((destructor));
947
#    endif
948
949
#  elif defined(__SUNPRO_C)
950
/*
951
 * Sun Studio compiler
952
 */
953
#    pragma fini(_PR_Fini)
954
static void _PR_Fini(void);
955
#  elif defined(AIX)
956
/* Need to use the -binitfini::_PR_Fini linker option. */
957
#  endif
958
959
0
void _PR_Fini(void) {
960
0
  void* thred;
961
0
  int rv;
962
963
0
  if (!_pr_initialized) {
964
    /* Either NSPR was never successfully initialized or
965
     * PR_Cleanup has been called already. */
966
0
    if (pt_book.keyCreated) {
967
0
      rv = pthread_key_delete(pt_book.key);
968
0
      PR_ASSERT(0 == rv);
969
0
      pt_book.keyCreated = PR_FALSE;
970
0
    }
971
0
    return;
972
0
  }
973
974
0
  _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
975
0
  if (NULL != thred) {
976
    /*
977
     * PR_FALSE, because it is unsafe to call back to the
978
     * thread private data destructors at final cleanup.
979
     */
980
0
    _pt_thread_death_internal(thred, PR_FALSE);
981
0
    rv = pthread_setspecific(pt_book.key, NULL);
982
0
    PR_ASSERT(0 == rv);
983
0
  }
984
0
  rv = pthread_key_delete(pt_book.key);
985
0
  PR_ASSERT(0 == rv);
986
0
  pt_book.keyCreated = PR_FALSE;
987
  /* TODO: free other resources used by NSPR */
988
  /* _pr_initialized = PR_FALSE; */
989
0
} /* _PR_Fini */
990
991
0
PR_IMPLEMENT(PRStatus) PR_Cleanup(void) {
992
0
  PRThread* me = PR_GetCurrentThread();
993
0
  int rv;
994
0
  PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
995
0
  PR_ASSERT(me->state & PT_THREAD_PRIMORD);
996
0
  if (me->state & PT_THREAD_PRIMORD) {
997
0
    PR_Lock(pt_book.ml);
998
0
    while (pt_book.user > pt_book.this_many) {
999
0
      PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
1000
0
    }
1001
0
    if (me->state & PT_THREAD_SYSTEM) {
1002
0
      pt_book.system -= 1;
1003
0
    } else {
1004
0
      pt_book.user -= 1;
1005
0
    }
1006
0
    PR_Unlock(pt_book.ml);
1007
1008
0
    _PR_MD_EARLY_CLEANUP();
1009
1010
0
    _PR_CleanupMW();
1011
0
    _PR_CleanupTime();
1012
0
    _PR_CleanupDtoa();
1013
0
    _PR_CleanupCallOnce();
1014
0
    _PR_ShutdownLinker();
1015
0
    _PR_LogCleanup();
1016
0
    _PR_CleanupNet();
1017
    /* Close all the fd's before calling _PR_CleanupIO */
1018
0
    _PR_CleanupIO();
1019
0
    _PR_CleanupCMon();
1020
1021
0
    _pt_thread_death(me);
1022
0
    rv = pthread_setspecific(pt_book.key, NULL);
1023
0
    PR_ASSERT(0 == rv);
1024
    /*
1025
     * I am not sure if it's safe to delete the cv and lock here,
1026
     * since there may still be "system" threads around. If this
1027
     * call isn't immediately prior to exiting, then there's a
1028
     * problem.
1029
     */
1030
0
    if (0 == pt_book.system) {
1031
0
      PR_DestroyCondVar(pt_book.cv);
1032
0
      pt_book.cv = NULL;
1033
0
      PR_DestroyLock(pt_book.ml);
1034
0
      pt_book.ml = NULL;
1035
0
    }
1036
0
    PR_DestroyLock(_pr_sleeplock);
1037
0
    _pr_sleeplock = NULL;
1038
0
    _PR_CleanupLayerCache();
1039
0
    _PR_CleanupEnv();
1040
0
#  ifdef _PR_ZONE_ALLOCATOR
1041
0
    _PR_DestroyZones();
1042
0
#  endif
1043
0
    _pr_initialized = PR_FALSE;
1044
0
    return PR_SUCCESS;
1045
0
  }
1046
0
  return PR_FAILURE;
1047
0
} /* PR_Cleanup */
1048
1049
0
PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) { _exit(status); }
1050
1051
0
PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread* thred) {
1052
0
  return (PRUint32)thred->id; /* and I don't know what they will do with it */
1053
0
}
1054
1055
/*
1056
 * $$$
1057
 * The following two thread-to-processor affinity functions are not
1058
 * yet implemented for pthreads.  By the way, these functions should return
1059
 * PRStatus rather than PRInt32 to indicate the success/failure status.
1060
 * $$$
1061
 */
1062
1063
PR_IMPLEMENT(PRInt32)
1064
0
PR_GetThreadAffinityMask(PRThread* thread, PRUint32* mask) {
1065
0
  return 0; /* not implemented */
1066
0
}
1067
1068
PR_IMPLEMENT(PRInt32)
1069
0
PR_SetThreadAffinityMask(PRThread* thread, PRUint32 mask) {
1070
0
  return 0; /* not implemented */
1071
0
}
1072
1073
PR_IMPLEMENT(void)
1074
0
PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void* arg) {
1075
0
  thread->dump = dump;
1076
0
  thread->dumpArg = arg;
1077
0
}
1078
1079
/*
1080
 * Garbage collection support follows.
1081
 */
1082
1083
/* a bogus signal mask for forcing a timed wait */
1084
/* Not so bogus in AIX as we really do a sigwait */
1085
static sigset_t sigwait_set;
1086
1087
static struct timespec onemillisec = {0, 1000000L};
1088
#  ifndef PT_NO_SIGTIMEDWAIT
1089
static struct timespec hundredmillisec = {0, 100000000L};
1090
#  endif
1091
1092
static void suspend_signal_handler(PRIntn sig);
1093
1094
#  ifdef PT_NO_SIGTIMEDWAIT
1095
static void null_signal_handler(PRIntn sig);
1096
#  endif
1097
1098
/*
1099
 * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which
1100
 * conflict with the use of these two signals in our GC support.
1101
 * So we don't know how to support GC on Linux pthreads.
1102
 */
1103
0
static void init_pthread_gc_support(void) {
1104
0
  PRIntn rv;
1105
1106
0
  {
1107
0
    struct sigaction sigact_usr2;
1108
1109
0
    sigact_usr2.sa_handler = suspend_signal_handler;
1110
0
    sigact_usr2.sa_flags = SA_RESTART;
1111
0
    sigemptyset(&sigact_usr2.sa_mask);
1112
1113
0
    rv = sigaction(SIGUSR2, &sigact_usr2, NULL);
1114
0
    PR_ASSERT(0 == rv);
1115
1116
0
    sigemptyset(&sigwait_set);
1117
0
#  if defined(PT_NO_SIGTIMEDWAIT)
1118
0
    sigaddset(&sigwait_set, SIGUSR1);
1119
#  else
1120
    sigaddset(&sigwait_set, SIGUSR2);
1121
#  endif /* defined(PT_NO_SIGTIMEDWAIT) */
1122
0
  }
1123
0
#  if defined(PT_NO_SIGTIMEDWAIT)
1124
0
  {
1125
0
    struct sigaction sigact_null;
1126
0
    sigact_null.sa_handler = null_signal_handler;
1127
0
    sigact_null.sa_flags = SA_RESTART;
1128
0
    sigemptyset(&sigact_null.sa_mask);
1129
0
    rv = sigaction(SIGUSR1, &sigact_null, NULL);
1130
0
    PR_ASSERT(0 == rv);
1131
0
  }
1132
0
#  endif /* defined(PT_NO_SIGTIMEDWAIT) */
1133
0
}
1134
1135
0
PR_IMPLEMENT(void) PR_SetThreadGCAble(void) {
1136
0
  PR_Lock(pt_book.ml);
1137
0
  PR_GetCurrentThread()->state |= PT_THREAD_GCABLE;
1138
0
  PR_Unlock(pt_book.ml);
1139
0
}
1140
1141
0
PR_IMPLEMENT(void) PR_ClearThreadGCAble(void) {
1142
0
  PR_Lock(pt_book.ml);
1143
0
  PR_GetCurrentThread()->state &= (~PT_THREAD_GCABLE);
1144
0
  PR_Unlock(pt_book.ml);
1145
0
}
1146
1147
#  if defined(DEBUG)
1148
static PRBool suspendAllOn = PR_FALSE;
1149
#  endif
1150
1151
static PRBool suspendAllSuspended = PR_FALSE;
1152
1153
0
PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void* arg) {
1154
0
  PRIntn count = 0;
1155
0
  PRStatus rv = PR_SUCCESS;
1156
0
  PRThread* thred = pt_book.first;
1157
1158
0
#  if defined(DEBUG) || defined(FORCE_PR_ASSERT)
1159
0
  PRThread* me = PR_GetCurrentThread();
1160
0
#  endif
1161
1162
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n"));
1163
  /*
1164
   * $$$
1165
   * Need to suspend all threads other than me before doing this.
1166
   * This is really a gross and disgusting thing to do. The only
1167
   * good thing is that since all other threads are suspended, holding
1168
   * the lock during a callback seems like child's play.
1169
   * $$$
1170
   */
1171
0
  PR_ASSERT(suspendAllOn);
1172
1173
0
  while (thred != NULL) {
1174
    /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
1175
     * qp->next after applying the function "func".  In particular, "func"
1176
     * might remove the thread from the queue and put it into another one in
1177
     * which case qp->next no longer points to the next entry in the original
1178
     * queue.
1179
     *
1180
     * To get around this problem, we save qp->next in qp_next before applying
1181
     * "func" and use that saved value as the next value after applying "func".
1182
     */
1183
0
    PRThread* next = thred->next;
1184
1185
0
    if (_PT_IS_GCABLE_THREAD(thred)) {
1186
0
      PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
1187
0
      PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1188
0
             ("In PR_EnumerateThreads callback thread %p thid = %X\n", thred,
1189
0
              thred->id));
1190
1191
0
      rv = func(thred, count++, arg);
1192
0
      if (rv != PR_SUCCESS) {
1193
0
        return rv;
1194
0
      }
1195
0
    }
1196
0
    thred = next;
1197
0
  }
1198
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1199
0
         ("End PR_EnumerateThreads count = %d \n", count));
1200
0
  return rv;
1201
0
} /* PR_EnumerateThreads */
1202
1203
/*
1204
 * PR_SuspendAll and PR_ResumeAll are called during garbage collection.  The
1205
 * strategy we use is to send a SIGUSR2 signal to every gc able thread that we
1206
 * intend to suspend. The signal handler will record the stack pointer and will
1207
 * block until resumed by the resume call.  Since the signal handler is the last
1208
 * routine called for the suspended thread, the stack pointer will also serve as
1209
 * a place where all the registers have been saved on the stack for the
1210
 * previously executing routines.
1211
 *
1212
 * Through global variables, we also make sure that PR_Suspend and PR_Resume
1213
 * does not proceed until the thread is suspended or resumed.
1214
 */
1215
1216
/*
1217
 * In the signal handler, we can not use condition variable notify or wait.
1218
 * This does not work consistently across all pthread platforms.  We also can
1219
 * not use locking since that does not seem to work reliably across platforms.
1220
 * Only thing we can do is yielding while testing for a global condition
1221
 * to change.  This does work on pthread supported platforms.  We may have
1222
 * to play with priortities if there are any problems detected.
1223
 */
1224
1225
/*
1226
 * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps
1227
 * pthread_yield. But that is horribly inefficient. Hence we use only sigwait,
1228
 * no sigtimedwait is available. We need to use another user signal, SIGUSR1.
1229
 * Actually SIGUSR1 is also used by exec in Java. So our usage here breaks the
1230
 * exec in Java, for AIX. You cannot use pthread_cond_wait or pthread_delay_np
1231
 * in the signal handler as all synchronization mechanisms just break down.
1232
 */
1233
1234
#  if defined(PT_NO_SIGTIMEDWAIT)
1235
0
static void null_signal_handler(PRIntn sig) { return; }
1236
#  endif
1237
1238
0
static void suspend_signal_handler(PRIntn sig) {
1239
0
  PRThread* me = PR_GetCurrentThread();
1240
1241
0
  PR_ASSERT(me != NULL);
1242
0
  PR_ASSERT(_PT_IS_GCABLE_THREAD(me));
1243
0
  PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0);
1244
1245
0
  PR_LOG(
1246
0
      _pr_gc_lm, PR_LOG_ALWAYS,
1247
0
      ("Begin suspend_signal_handler thred %p thread id = %X\n", me, me->id));
1248
1249
  /*
1250
   * save stack pointer
1251
   */
1252
0
  me->sp = &me;
1253
1254
  /*
1255
     At this point, the thread's stack pointer has been saved,
1256
     And it is going to enter a wait loop until it is resumed.
1257
     So it is _really_ suspended
1258
  */
1259
1260
0
  me->suspend |= PT_THREAD_SUSPENDED;
1261
1262
  /*
1263
   * now, block current thread
1264
   */
1265
0
#  if defined(PT_NO_SIGTIMEDWAIT)
1266
0
  pthread_cond_signal(&me->suspendResumeCV);
1267
0
  while (me->suspend & PT_THREAD_SUSPENDED) {
1268
0
#    if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) && \
1269
0
        !defined(DARWIN) && !defined(RISCOS)
1270
0
    PRIntn rv;
1271
0
    sigwait(&sigwait_set, &rv);
1272
0
#    endif
1273
0
  }
1274
0
  me->suspend |= PT_THREAD_RESUMED;
1275
0
  pthread_cond_signal(&me->suspendResumeCV);
1276
#  else /* defined(PT_NO_SIGTIMEDWAIT) */
1277
  while (me->suspend & PT_THREAD_SUSPENDED) {
1278
    PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec);
1279
    PR_ASSERT(-1 == rv);
1280
  }
1281
  me->suspend |= PT_THREAD_RESUMED;
1282
#  endif
1283
1284
  /*
1285
   * At this point, thread has been resumed, so set a global condition.
1286
   * The ResumeAll needs to know that this has really been resumed.
1287
   * So the signal handler sets a flag which PR_ResumeAll will reset.
1288
   * The PR_ResumeAll must reset this flag ...
1289
   */
1290
1291
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1292
0
         ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id));
1293
0
} /* suspend_signal_handler */
1294
1295
0
static void pt_SuspendSet(PRThread* thred) {
1296
0
  PRIntn rv;
1297
1298
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1299
0
         ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id));
1300
1301
  /*
1302
   * Check the thread state and signal the thread to suspend
1303
   */
1304
1305
0
  PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0);
1306
1307
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1308
0
         ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n", thred,
1309
0
          thred->id));
1310
0
  rv = pthread_kill(thred->id, SIGUSR2);
1311
0
  PR_ASSERT(0 == rv);
1312
0
}
1313
1314
0
static void pt_SuspendTest(PRThread* thred) {
1315
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1316
0
         ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id));
1317
1318
  /*
1319
   * Wait for the thread to be really suspended. This happens when the
1320
   * suspend signal handler stores the stack pointer and sets the state
1321
   * to suspended.
1322
   */
1323
1324
0
#  if defined(PT_NO_SIGTIMEDWAIT)
1325
0
  pthread_mutex_lock(&thred->suspendResumeMutex);
1326
0
  while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) {
1327
0
    pthread_cond_timedwait(&thred->suspendResumeCV, &thred->suspendResumeMutex,
1328
0
                           &onemillisec);
1329
0
  }
1330
0
  pthread_mutex_unlock(&thred->suspendResumeMutex);
1331
#  else
1332
  while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) {
1333
    PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
1334
    PR_ASSERT(-1 == rv);
1335
  }
1336
#  endif
1337
1338
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1339
0
         ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id));
1340
0
} /* pt_SuspendTest */
1341
1342
0
static void pt_ResumeSet(PRThread* thred) {
1343
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1344
0
         ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id));
1345
1346
  /*
1347
   * Clear the global state and set the thread state so that it will
1348
   * continue past yield loop in the suspend signal handler
1349
   */
1350
1351
0
  PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED);
1352
1353
0
  thred->suspend &= ~PT_THREAD_SUSPENDED;
1354
1355
0
#  if defined(PT_NO_SIGTIMEDWAIT)
1356
0
  pthread_kill(thred->id, SIGUSR1);
1357
0
#  endif
1358
1359
0
} /* pt_ResumeSet */
1360
1361
0
static void pt_ResumeTest(PRThread* thred) {
1362
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1363
0
         ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id));
1364
1365
  /*
1366
   * Wait for the threads resume state to change
1367
   * to indicate it is really resumed
1368
   */
1369
0
#  if defined(PT_NO_SIGTIMEDWAIT)
1370
0
  pthread_mutex_lock(&thred->suspendResumeMutex);
1371
0
  while ((thred->suspend & PT_THREAD_RESUMED) == 0) {
1372
0
    pthread_cond_timedwait(&thred->suspendResumeCV, &thred->suspendResumeMutex,
1373
0
                           &onemillisec);
1374
0
  }
1375
0
  pthread_mutex_unlock(&thred->suspendResumeMutex);
1376
#  else
1377
  while ((thred->suspend & PT_THREAD_RESUMED) == 0) {
1378
    PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
1379
    PR_ASSERT(-1 == rv);
1380
  }
1381
#  endif
1382
1383
0
  thred->suspend &= ~PT_THREAD_RESUMED;
1384
1385
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1386
0
         ("End pt_ResumeTest thred %p tid %X\n", thred, thred->id));
1387
0
} /* pt_ResumeTest */
1388
1389
static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT;
1390
1391
0
PR_IMPLEMENT(void) PR_SuspendAll(void) {
1392
0
#  ifdef DEBUG
1393
0
  PRIntervalTime stime, etime;
1394
0
#  endif
1395
0
  PRThread* thred = pt_book.first;
1396
0
  PRThread* me = PR_GetCurrentThread();
1397
0
  int rv;
1398
1399
0
  rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
1400
0
  PR_ASSERT(0 == rv);
1401
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
1402
  /*
1403
   * Stop all threads which are marked GC able.
1404
   */
1405
0
  PR_Lock(pt_book.ml);
1406
0
#  ifdef DEBUG
1407
0
  suspendAllOn = PR_TRUE;
1408
0
  stime = PR_IntervalNow();
1409
0
#  endif
1410
0
  while (thred != NULL) {
1411
0
    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1412
0
      pt_SuspendSet(thred);
1413
0
    }
1414
0
    thred = thred->next;
1415
0
  }
1416
1417
  /* Wait till they are really suspended */
1418
0
  thred = pt_book.first;
1419
0
  while (thred != NULL) {
1420
0
    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1421
0
      pt_SuspendTest(thred);
1422
0
    }
1423
0
    thred = thred->next;
1424
0
  }
1425
1426
0
  suspendAllSuspended = PR_TRUE;
1427
1428
0
#  ifdef DEBUG
1429
0
  etime = PR_IntervalNow();
1430
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1431
0
         ("End PR_SuspendAll (time %dms)\n",
1432
0
          PR_IntervalToMilliseconds(etime - stime)));
1433
0
#  endif
1434
0
} /* PR_SuspendAll */
1435
1436
0
PR_IMPLEMENT(void) PR_ResumeAll(void) {
1437
0
#  ifdef DEBUG
1438
0
  PRIntervalTime stime, etime;
1439
0
#  endif
1440
0
  PRThread* thred = pt_book.first;
1441
0
  PRThread* me = PR_GetCurrentThread();
1442
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
1443
  /*
1444
   * Resume all previously suspended GC able threads.
1445
   */
1446
0
  suspendAllSuspended = PR_FALSE;
1447
0
#  ifdef DEBUG
1448
0
  stime = PR_IntervalNow();
1449
0
#  endif
1450
1451
0
  while (thred != NULL) {
1452
0
    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1453
0
      pt_ResumeSet(thred);
1454
0
    }
1455
0
    thred = thred->next;
1456
0
  }
1457
1458
0
  thred = pt_book.first;
1459
0
  while (thred != NULL) {
1460
0
    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1461
0
      pt_ResumeTest(thred);
1462
0
    }
1463
0
    thred = thred->next;
1464
0
  }
1465
1466
0
  PR_Unlock(pt_book.ml);
1467
0
#  ifdef DEBUG
1468
0
  suspendAllOn = PR_FALSE;
1469
0
  etime = PR_IntervalNow();
1470
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1471
0
         ("End PR_ResumeAll (time %dms)\n",
1472
0
          PR_IntervalToMilliseconds(etime - stime)));
1473
0
#  endif
1474
0
} /* PR_ResumeAll */
1475
1476
/* Return the stack pointer for the given thread- used by the GC */
1477
0
PR_IMPLEMENT(void*) PR_GetSP(PRThread* thred) {
1478
0
  PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1479
0
         ("in PR_GetSP thred %p thid = %X, sp = %p\n", thred, thred->id,
1480
0
          thred->sp));
1481
0
  return thred->sp;
1482
0
} /* PR_GetSP */
1483
1484
0
PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char* name) {
1485
0
  PRThread* thread;
1486
0
  size_t nameLen;
1487
0
  int result = 0;
1488
1489
0
  if (!name) {
1490
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1491
0
    return PR_FAILURE;
1492
0
  }
1493
1494
0
  thread = PR_GetCurrentThread();
1495
0
  if (!thread) {
1496
0
    return PR_FAILURE;
1497
0
  }
1498
1499
0
  PR_Free(thread->name);
1500
0
  nameLen = strlen(name);
1501
0
  thread->name = (char*)PR_Malloc(nameLen + 1);
1502
0
  if (!thread->name) {
1503
0
    return PR_FAILURE;
1504
0
  }
1505
0
  memcpy(thread->name, name, nameLen + 1);
1506
1507
#  if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)
1508
  pthread_set_name_np(thread->id, name);
1509
#  elif defined(ANDROID)
1510
  prctl(PR_SET_NAME, (unsigned long)(name));
1511
#  elif defined(NETBSD)
1512
  result = pthread_setname_np(thread->id, "%s", (void*)name);
1513
#  else /* not BSD */
1514
  /*
1515
   * On OSX, pthread_setname_np is only available in 10.6 or later, so test
1516
   * for it at runtime.  It also may not be available on all linux distros.
1517
   */
1518
#    if defined(DARWIN)
1519
  int (*dynamic_pthread_setname_np)(const char*);
1520
#    else
1521
0
  int (*dynamic_pthread_setname_np)(pthread_t, const char*);
1522
0
#    endif
1523
1524
0
  *(void**)(&dynamic_pthread_setname_np) =
1525
0
      dlsym(RTLD_DEFAULT, "pthread_setname_np");
1526
0
  if (!dynamic_pthread_setname_np) {
1527
0
    return PR_SUCCESS;
1528
0
  }
1529
1530
#    if defined(DARWIN)
1531
  /* Mac OS X has a length limit of 63 characters, but there is no API
1532
   * exposing it.
1533
   */
1534
#      define SETNAME_LENGTH_CONSTRAINT 63
1535
#    else
1536
  /*
1537
   * The 15-character name length limit is an experimentally determined
1538
   * length of a null-terminated string that most linux distros accept
1539
   * as an argument to pthread_setname_np.  Otherwise the E2BIG
1540
   * error is returned by the function.
1541
   */
1542
0
#      define SETNAME_LENGTH_CONSTRAINT 15
1543
0
#    endif
1544
0
#    define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1)
1545
0
#    define SETNAME_FRAGMENT2_LENGTH \
1546
0
      (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1)
1547
0
  char name_dup[SETNAME_LENGTH_CONSTRAINT + 1];
1548
0
  if (nameLen > SETNAME_LENGTH_CONSTRAINT) {
1549
0
    memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH);
1550
0
    name_dup[SETNAME_FRAGMENT1_LENGTH] = '~';
1551
    /* Note that this also copies the null terminator. */
1552
0
    memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1,
1553
0
           name + nameLen - SETNAME_FRAGMENT2_LENGTH,
1554
0
           SETNAME_FRAGMENT2_LENGTH + 1);
1555
0
    name = name_dup;
1556
0
  }
1557
1558
#    if defined(DARWIN)
1559
  result = dynamic_pthread_setname_np(name);
1560
#    else
1561
0
  result = dynamic_pthread_setname_np(thread->id, name);
1562
0
#    endif
1563
0
#  endif /* not BSD */
1564
1565
0
  if (result) {
1566
0
    PR_SetError(PR_UNKNOWN_ERROR, result);
1567
0
    return PR_FAILURE;
1568
0
  }
1569
0
  return PR_SUCCESS;
1570
0
}
1571
1572
0
PR_IMPLEMENT(const char*) PR_GetThreadName(const PRThread* thread) {
1573
0
  if (!thread) {
1574
0
    return NULL;
1575
0
  }
1576
0
  return thread->name;
1577
0
}
1578
1579
#endif /* defined(_PR_PTHREADS) */
1580
1581
/* ptthread.c */