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