Coverage Report

Created: 2026-10-06 06:17

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