| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "primpl.h" |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <signal.h> |
| #include <pthread.h> |
| |
| |
| sigset_t ints_off; |
| pthread_mutex_t _pr_heapLock; |
| pthread_key_t current_thread_key; |
| pthread_key_t current_cpu_key; |
| pthread_key_t last_thread_key; |
| pthread_key_t intsoff_key; |
| |
| |
| PRInt32 _pr_md_pthreads_created, _pr_md_pthreads_failed; |
| PRInt32 _pr_md_pthreads = 1; |
| |
| void _MD_EarlyInit(void) |
| { |
| extern PRInt32 _nspr_noclock; |
| |
| if (pthread_key_create(¤t_thread_key, NULL) != 0) { |
| perror("pthread_key_create failed"); |
| exit(1); |
| } |
| if (pthread_key_create(¤t_cpu_key, NULL) != 0) { |
| perror("pthread_key_create failed"); |
| exit(1); |
| } |
| if (pthread_key_create(&last_thread_key, NULL) != 0) { |
| perror("pthread_key_create failed"); |
| exit(1); |
| } |
| if (pthread_key_create(&intsoff_key, NULL) != 0) { |
| perror("pthread_key_create failed"); |
| exit(1); |
| } |
| |
| sigemptyset(&ints_off); |
| sigaddset(&ints_off, SIGALRM); |
| sigaddset(&ints_off, SIGIO); |
| sigaddset(&ints_off, SIGCLD); |
| |
| /* |
| * disable clock interrupts |
| */ |
| _nspr_noclock = 1; |
| |
| } |
| |
| void _MD_InitLocks() |
| { |
| if (pthread_mutex_init(&_pr_heapLock, NULL) != 0) { |
| perror("pthread_mutex_init failed"); |
| exit(1); |
| } |
| } |
| |
| PR_IMPLEMENT(void) _MD_FREE_LOCK(struct _MDLock *lockp) |
| { |
| PRIntn _is; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| |
| if (me && !_PR_IS_NATIVE_THREAD(me)) |
| _PR_INTSOFF(_is); |
| pthread_mutex_destroy(&lockp->mutex); |
| if (me && !_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(_is); |
| } |
| |
| |
| |
| PR_IMPLEMENT(PRStatus) _MD_NEW_LOCK(struct _MDLock *lockp) |
| { |
| PRStatus rv; |
| PRIntn is; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| |
| if (me && !_PR_IS_NATIVE_THREAD(me)) |
| _PR_INTSOFF(is); |
| rv = pthread_mutex_init(&lockp->mutex, NULL); |
| if (me && !_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(is); |
| return (rv == 0) ? PR_SUCCESS : PR_FAILURE; |
| } |
| |
| |
| PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) |
| { |
| if (isCurrent) { |
| (void) setjmp(CONTEXT(t)); |
| } |
| *np = sizeof(CONTEXT(t)) / sizeof(PRWord); |
| return (PRWord *) CONTEXT(t); |
| } |
| |
| PR_IMPLEMENT(void) |
| _MD_SetPriority(_MDThread *thread, PRThreadPriority newPri) |
| { |
| /* |
| * XXX - to be implemented |
| */ |
| return; |
| } |
| |
| PR_IMPLEMENT(PRStatus) _MD_InitThread(struct PRThread *thread) |
| { |
| struct sigaction sigact; |
| |
| if (thread->flags & _PR_GLOBAL_SCOPE) { |
| thread->md.pthread = pthread_self(); |
| #if 0 |
| /* |
| * set up SIGUSR1 handler; this is used to save state |
| * during PR_SuspendAll |
| */ |
| sigact.sa_handler = save_context_and_block; |
| sigact.sa_flags = SA_RESTART; |
| /* |
| * Must mask clock interrupts |
| */ |
| sigact.sa_mask = timer_set; |
| sigaction(SIGUSR1, &sigact, 0); |
| #endif |
| } |
| |
| return PR_SUCCESS; |
| } |
| |
| PR_IMPLEMENT(void) _MD_ExitThread(struct PRThread *thread) |
| { |
| if (thread->flags & _PR_GLOBAL_SCOPE) { |
| _MD_CLEAN_THREAD(thread); |
| _MD_SET_CURRENT_THREAD(NULL); |
| } |
| } |
| |
| PR_IMPLEMENT(void) _MD_CleanThread(struct PRThread *thread) |
| { |
| if (thread->flags & _PR_GLOBAL_SCOPE) { |
| pthread_mutex_destroy(&thread->md.pthread_mutex); |
| pthread_cond_destroy(&thread->md.pthread_cond); |
| } |
| } |
| |
| PR_IMPLEMENT(void) _MD_SuspendThread(struct PRThread *thread) |
| { |
| PRInt32 rv; |
| |
| PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && |
| _PR_IS_GCABLE_THREAD(thread)); |
| #if 0 |
| thread->md.suspending_id = getpid(); |
| rv = kill(thread->md.id, SIGUSR1); |
| PR_ASSERT(rv == 0); |
| /* |
| * now, block the current thread/cpu until woken up by the suspended |
| * thread from it's SIGUSR1 signal handler |
| */ |
| blockproc(getpid()); |
| #endif |
| } |
| |
| PR_IMPLEMENT(void) _MD_ResumeThread(struct PRThread *thread) |
| { |
| PRInt32 rv; |
| |
| PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && |
| _PR_IS_GCABLE_THREAD(thread)); |
| #if 0 |
| rv = unblockproc(thread->md.id); |
| #endif |
| } |
| |
| PR_IMPLEMENT(void) _MD_SuspendCPU(struct _PRCPU *thread) |
| { |
| PRInt32 rv; |
| |
| #if 0 |
| cpu->md.suspending_id = getpid(); |
| rv = kill(cpu->md.id, SIGUSR1); |
| PR_ASSERT(rv == 0); |
| /* |
| * now, block the current thread/cpu until woken up by the suspended |
| * thread from it's SIGUSR1 signal handler |
| */ |
| blockproc(getpid()); |
| #endif |
| } |
| |
| PR_IMPLEMENT(void) _MD_ResumeCPU(struct _PRCPU *thread) |
| { |
| #if 0 |
| unblockproc(cpu->md.id); |
| #endif |
| } |
| |
| |
| #define PT_NANOPERMICRO 1000UL |
| #define PT_BILLION 1000000000UL |
| |
| PR_IMPLEMENT(PRStatus) |
| _pt_wait(PRThread *thread, PRIntervalTime timeout) |
| { |
| int rv; |
| struct timeval now; |
| struct timespec tmo; |
| PRUint32 ticks = PR_TicksPerSecond(); |
| |
| |
| if (timeout != PR_INTERVAL_NO_TIMEOUT) { |
| tmo.tv_sec = timeout / ticks; |
| tmo.tv_nsec = timeout - (tmo.tv_sec * ticks); |
| tmo.tv_nsec = PR_IntervalToMicroseconds(PT_NANOPERMICRO * |
| tmo.tv_nsec); |
| |
| /* pthreads wants this in absolute time, off we go ... */ |
| (void)GETTIMEOFDAY(&now); |
| /* that one's usecs, this one's nsecs - grrrr! */ |
| tmo.tv_sec += now.tv_sec; |
| tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec); |
| tmo.tv_sec += tmo.tv_nsec / PT_BILLION; |
| tmo.tv_nsec %= PT_BILLION; |
| } |
| |
| pthread_mutex_lock(&thread->md.pthread_mutex); |
| thread->md.wait--; |
| if (thread->md.wait < 0) { |
| if (timeout != PR_INTERVAL_NO_TIMEOUT) { |
| rv = pthread_cond_timedwait(&thread->md.pthread_cond, |
| &thread->md.pthread_mutex, &tmo); |
| } |
| else |
| rv = pthread_cond_wait(&thread->md.pthread_cond, |
| &thread->md.pthread_mutex); |
| if (rv != 0) { |
| thread->md.wait++; |
| } |
| } else |
| rv = 0; |
| pthread_mutex_unlock(&thread->md.pthread_mutex); |
| |
| return (rv == 0) ? PR_SUCCESS : PR_FAILURE; |
| } |
| |
| PR_IMPLEMENT(PRStatus) |
| _MD_wait(PRThread *thread, PRIntervalTime ticks) |
| { |
| if ( thread->flags & _PR_GLOBAL_SCOPE ) { |
| _MD_CHECK_FOR_EXIT(); |
| if (_pt_wait(thread, ticks) == PR_FAILURE) { |
| _MD_CHECK_FOR_EXIT(); |
| /* |
| * wait timed out |
| */ |
| _PR_THREAD_LOCK(thread); |
| if (thread->wait.cvar) { |
| /* |
| * The thread will remove itself from the waitQ |
| * of the cvar in _PR_WaitCondVar |
| */ |
| thread->wait.cvar = NULL; |
| thread->state = _PR_RUNNING; |
| _PR_THREAD_UNLOCK(thread); |
| } else { |
| _pt_wait(thread, PR_INTERVAL_NO_TIMEOUT); |
| _PR_THREAD_UNLOCK(thread); |
| } |
| } |
| } else { |
| _PR_MD_SWITCH_CONTEXT(thread); |
| } |
| return PR_SUCCESS; |
| } |
| |
| PR_IMPLEMENT(PRStatus) |
| _MD_WakeupWaiter(PRThread *thread) |
| { |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| PRInt32 pid, rv; |
| PRIntn is; |
| |
| PR_ASSERT(_pr_md_idle_cpus >= 0); |
| if (thread == NULL) { |
| if (_pr_md_idle_cpus) |
| _MD_Wakeup_CPUs(); |
| } else if (!_PR_IS_NATIVE_THREAD(thread)) { |
| /* |
| * If the thread is on my cpu's runq there is no need to |
| * wakeup any cpus |
| */ |
| if (!_PR_IS_NATIVE_THREAD(me)) { |
| if (me->cpu != thread->cpu) { |
| if (_pr_md_idle_cpus) |
| _MD_Wakeup_CPUs(); |
| } |
| } else { |
| if (_pr_md_idle_cpus) |
| _MD_Wakeup_CPUs(); |
| } |
| } else { |
| PR_ASSERT(_PR_IS_NATIVE_THREAD(thread)); |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_INTSOFF(is); |
| |
| pthread_mutex_lock(&thread->md.pthread_mutex); |
| thread->md.wait++; |
| rv = pthread_cond_signal(&thread->md.pthread_cond); |
| PR_ASSERT(rv == 0); |
| pthread_mutex_unlock(&thread->md.pthread_mutex); |
| |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(is); |
| } |
| return PR_SUCCESS; |
| } |
| |
| /* These functions should not be called for AIX */ |
| PR_IMPLEMENT(void) |
| _MD_YIELD(void) |
| { |
| PR_NOT_REACHED("_MD_YIELD should not be called for AIX."); |
| } |
| |
| PR_IMPLEMENT(PRStatus) |
| _MD_CreateThread( |
| PRThread *thread, |
| void (*start) (void *), |
| PRThreadPriority priority, |
| PRThreadScope scope, |
| PRThreadState state, |
| PRUint32 stackSize) |
| { |
| PRIntn is; |
| int rv; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| pthread_attr_t attr; |
| |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_INTSOFF(is); |
| |
| if (pthread_mutex_init(&thread->md.pthread_mutex, NULL) != 0) { |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(is); |
| return PR_FAILURE; |
| } |
| |
| if (pthread_cond_init(&thread->md.pthread_cond, NULL) != 0) { |
| pthread_mutex_destroy(&thread->md.pthread_mutex); |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(is); |
| return PR_FAILURE; |
| } |
| thread->flags |= _PR_GLOBAL_SCOPE; |
| |
| pthread_attr_init(&attr); /* initialize attr with default attributes */ |
| if (pthread_attr_setstacksize(&attr, (size_t) stackSize) != 0) { |
| pthread_mutex_destroy(&thread->md.pthread_mutex); |
| pthread_cond_destroy(&thread->md.pthread_cond); |
| pthread_attr_destroy(&attr); |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(is); |
| return PR_FAILURE; |
| } |
| |
| thread->md.wait = 0; |
| rv = pthread_create(&thread->md.pthread, &attr, start, (void *)thread); |
| if (0 == rv) { |
| _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_created); |
| _MD_ATOMIC_INCREMENT(&_pr_md_pthreads); |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(is); |
| return PR_SUCCESS; |
| } else { |
| pthread_mutex_destroy(&thread->md.pthread_mutex); |
| pthread_cond_destroy(&thread->md.pthread_cond); |
| pthread_attr_destroy(&attr); |
| _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_failed); |
| if (!_PR_IS_NATIVE_THREAD(me)) |
| _PR_FAST_INTSON(is); |
| PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, rv); |
| return PR_FAILURE; |
| } |
| } |
| |
| PR_IMPLEMENT(void) |
| _MD_InitRunningCPU(struct _PRCPU *cpu) |
| { |
| extern int _pr_md_pipefd[2]; |
| |
| _MD_unix_init_running_cpu(cpu); |
| cpu->md.pthread = pthread_self(); |
| if (_pr_md_pipefd[0] >= 0) { |
| _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0]; |
| #ifndef _PR_USE_POLL |
| FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu)); |
| #endif |
| } |
| } |
| |
| |
| void |
| _MD_CleanupBeforeExit(void) |
| { |
| #if 0 |
| extern PRInt32 _pr_cpus_exit; |
| |
| _pr_irix_exit_now = 1; |
| if (_pr_numCPU > 1) { |
| /* |
| * Set a global flag, and wakeup all cpus which will notice the flag |
| * and exit. |
| */ |
| _pr_cpus_exit = getpid(); |
| _MD_Wakeup_CPUs(); |
| while(_pr_numCPU > 1) { |
| _PR_WAIT_SEM(_pr_irix_exit_sem); |
| _pr_numCPU--; |
| } |
| } |
| /* |
| * cause global threads on the recycle list to exit |
| */ |
| _PR_DEADQ_LOCK; |
| if (_PR_NUM_DEADNATIVE != 0) { |
| PRThread *thread; |
| PRCList *ptr; |
| |
| ptr = _PR_DEADNATIVEQ.next; |
| while( ptr != &_PR_DEADNATIVEQ ) { |
| thread = _PR_THREAD_PTR(ptr); |
| _MD_CVAR_POST_SEM(thread); |
| ptr = ptr->next; |
| } |
| } |
| _PR_DEADQ_UNLOCK; |
| while(_PR_NUM_DEADNATIVE > 1) { |
| _PR_WAIT_SEM(_pr_irix_exit_sem); |
| _PR_DEC_DEADNATIVE; |
| } |
| #endif |
| } |