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