michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "primpl.h" michael@0: michael@0: #if !defined (USE_SVR4_THREADS) michael@0: michael@0: /* michael@0: * using only NSPR threads here michael@0: */ michael@0: michael@0: #include michael@0: michael@0: void _MD_EarlyInit(void) michael@0: { michael@0: } michael@0: michael@0: PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) michael@0: { michael@0: if (isCurrent) { michael@0: (void) setjmp(CONTEXT(t)); michael@0: } michael@0: *np = sizeof(CONTEXT(t)) / sizeof(PRWord); michael@0: return (PRWord *) CONTEXT(t); michael@0: } michael@0: michael@0: #ifdef ALARMS_BREAK_TCP /* I don't think they do */ michael@0: michael@0: PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, michael@0: PRIntervalTime timeout) michael@0: { michael@0: PRInt32 rv; michael@0: michael@0: _MD_BLOCK_CLOCK_INTERRUPTS(); michael@0: rv = _connect(osfd,addr,addrlen); michael@0: _MD_UNBLOCK_CLOCK_INTERRUPTS(); michael@0: } michael@0: michael@0: PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, michael@0: PRIntervalTime timeout) michael@0: { michael@0: PRInt32 rv; michael@0: michael@0: _MD_BLOCK_CLOCK_INTERRUPTS(); michael@0: rv = _accept(osfd,addr,addrlen); michael@0: _MD_UNBLOCK_CLOCK_INTERRUPTS(); michael@0: return(rv); michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * These are also implemented in pratom.c using NSPR locks. Any reason michael@0: * this might be better or worse? If you like this better, define michael@0: * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h michael@0: */ michael@0: #ifdef _PR_HAVE_ATOMIC_OPS michael@0: /* Atomic operations */ michael@0: #include michael@0: static FILE *_uw_semf; michael@0: michael@0: void michael@0: _MD_INIT_ATOMIC(void) michael@0: { michael@0: /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */ michael@0: if ((_uw_semf = tmpfile()) == NULL) michael@0: PR_ASSERT(0); michael@0: michael@0: return; michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_INCREMENT(PRInt32 *val) michael@0: { michael@0: flockfile(_uw_semf); michael@0: (*val)++; michael@0: unflockfile(_uw_semf); michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) michael@0: { michael@0: flockfile(_uw_semf); michael@0: (*ptr) += val; michael@0: unflockfile(_uw_semf); michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_DECREMENT(PRInt32 *val) michael@0: { michael@0: flockfile(_uw_semf); michael@0: (*val)--; michael@0: unflockfile(_uw_semf); michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) michael@0: { michael@0: flockfile(_uw_semf); michael@0: *val = newval; michael@0: unflockfile(_uw_semf); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: _MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: PRStatus michael@0: _MD_InitializeThread(PRThread *thread) michael@0: { michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRStatus michael@0: _MD_WAIT(PRThread *thread, PRIntervalTime ticks) michael@0: { michael@0: PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); michael@0: _PR_MD_SWITCH_CONTEXT(thread); michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRStatus michael@0: _MD_WAKEUP_WAITER(PRThread *thread) michael@0: { michael@0: if (thread) { michael@0: PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); michael@0: } michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: /* These functions should not be called for Unixware */ michael@0: void michael@0: _MD_YIELD(void) michael@0: { michael@0: PR_NOT_REACHED("_MD_YIELD should not be called for Unixware."); michael@0: } michael@0: michael@0: PRStatus michael@0: _MD_CREATE_THREAD( michael@0: PRThread *thread, michael@0: void (*start) (void *), michael@0: PRThreadPriority priority, michael@0: PRThreadScope scope, michael@0: PRThreadState state, michael@0: PRUint32 stackSize) michael@0: { michael@0: PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware."); michael@0: } michael@0: michael@0: #else /* USE_SVR4_THREADS */ michael@0: michael@0: /* NOTE: michael@0: * SPARC v9 (Ultras) do have an atomic test-and-set operation. But michael@0: * SPARC v8 doesn't. We should detect in the init if we are running on michael@0: * v8 or v9, and then use assembly where we can. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: static mutex_t _unixware_atomic = DEFAULTMUTEX; michael@0: michael@0: #define TEST_THEN_ADD(where, inc) \ michael@0: if (mutex_lock(&_unixware_atomic) != 0)\ michael@0: PR_ASSERT(0);\ michael@0: *where += inc;\ michael@0: if (mutex_unlock(&_unixware_atomic) != 0)\ michael@0: PR_ASSERT(0); michael@0: michael@0: #define TEST_THEN_SET(where, val) \ michael@0: if (mutex_lock(&_unixware_atomic) != 0)\ michael@0: PR_ASSERT(0);\ michael@0: *where = val;\ michael@0: if (mutex_unlock(&_unixware_atomic) != 0)\ michael@0: PR_ASSERT(0); michael@0: michael@0: void michael@0: _MD_INIT_ATOMIC(void) michael@0: { michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_INCREMENT(PRInt32 *val) michael@0: { michael@0: TEST_THEN_ADD(val, 1); michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) michael@0: { michael@0: TEST_THEN_ADD(ptr, val); michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_DECREMENT(PRInt32 *val) michael@0: { michael@0: TEST_THEN_ADD(val, 0xffffffff); michael@0: } michael@0: michael@0: void michael@0: _MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) michael@0: { michael@0: TEST_THEN_SET(val, newval); michael@0: } michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: michael@0: THREAD_KEY_T threadid_key; michael@0: THREAD_KEY_T cpuid_key; michael@0: THREAD_KEY_T last_thread_key; michael@0: static sigset_t set, oldset; michael@0: michael@0: void _MD_EarlyInit(void) michael@0: { michael@0: THR_KEYCREATE(&threadid_key, NULL); michael@0: THR_KEYCREATE(&cpuid_key, NULL); michael@0: THR_KEYCREATE(&last_thread_key, NULL); michael@0: sigemptyset(&set); michael@0: sigaddset(&set, SIGALRM); michael@0: } michael@0: michael@0: PRStatus _MD_CREATE_THREAD(PRThread *thread, michael@0: void (*start)(void *), michael@0: PRThreadPriority priority, michael@0: PRThreadScope scope, michael@0: PRThreadState state, michael@0: PRUint32 stackSize) michael@0: { michael@0: long flags; michael@0: michael@0: /* mask out SIGALRM for native thread creation */ michael@0: thr_sigsetmask(SIG_BLOCK, &set, &oldset); michael@0: michael@0: flags = (state == PR_JOINABLE_THREAD ? THR_SUSPENDED/*|THR_NEW_LWP*/ michael@0: : THR_SUSPENDED|THR_DETACHED/*|THR_NEW_LWP*/); michael@0: if (_PR_IS_GCABLE_THREAD(thread) || michael@0: (scope == PR_GLOBAL_BOUND_THREAD)) michael@0: flags |= THR_BOUND; michael@0: michael@0: if (thr_create(NULL, thread->stack->stackSize, michael@0: (void *(*)(void *)) start, (void *) thread, michael@0: flags, michael@0: &thread->md.handle)) { michael@0: thr_sigsetmask(SIG_SETMASK, &oldset, NULL); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: michael@0: /* When the thread starts running, then the lwpid is set to the right michael@0: * value. Until then we want to mark this as 'uninit' so that michael@0: * its register state is initialized properly for GC */ michael@0: michael@0: thread->md.lwpid = -1; michael@0: thr_sigsetmask(SIG_SETMASK, &oldset, NULL); michael@0: _MD_NEW_SEM(&thread->md.waiter_sem, 0); michael@0: michael@0: if ((scope == PR_GLOBAL_THREAD) || (scope == PR_GLOBAL_BOUND_THREAD)) { michael@0: thread->flags |= _PR_GLOBAL_SCOPE; michael@0: } michael@0: michael@0: /* michael@0: ** Set the thread priority. This will also place the thread on michael@0: ** the runQ. michael@0: ** michael@0: ** Force PR_SetThreadPriority to set the priority by michael@0: ** setting thread->priority to 100. michael@0: */ michael@0: { michael@0: int pri; michael@0: pri = thread->priority; michael@0: thread->priority = 100; michael@0: PR_SetThreadPriority( thread, pri ); michael@0: michael@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, michael@0: ("(0X%x)[Start]: on to runq at priority %d", michael@0: thread, thread->priority)); michael@0: } michael@0: michael@0: /* Activate the thread */ michael@0: if (thr_continue( thread->md.handle ) ) { michael@0: return PR_FAILURE; michael@0: } michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: void _MD_cleanup_thread(PRThread *thread) michael@0: { michael@0: thread_t hdl; michael@0: PRMonitor *mon; michael@0: michael@0: hdl = thread->md.handle; michael@0: michael@0: /* michael@0: ** First, suspend the thread (unless it's the active one) michael@0: ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to michael@0: ** prevent both of us modifying the thread structure at the same time. michael@0: */ michael@0: if ( thread != _PR_MD_CURRENT_THREAD() ) { michael@0: thr_suspend(hdl); michael@0: } michael@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, michael@0: ("(0X%x)[DestroyThread]\n", thread)); michael@0: michael@0: _MD_DESTROY_SEM(&thread->md.waiter_sem); michael@0: } michael@0: michael@0: void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri) michael@0: { michael@0: if(thr_setprio((thread_t)md_thread->handle, newPri)) { michael@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, michael@0: ("_PR_SetThreadPriority: can't set thread priority\n")); michael@0: } michael@0: } michael@0: michael@0: void _MD_WAIT_CV( michael@0: struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout) michael@0: { michael@0: struct timespec tt; michael@0: PRUint32 msec; michael@0: int rv; michael@0: PRThread *me = _PR_MD_CURRENT_THREAD(); michael@0: michael@0: msec = PR_IntervalToMilliseconds(timeout); michael@0: michael@0: GETTIME (&tt); michael@0: michael@0: tt.tv_sec += msec / PR_MSEC_PER_SEC; michael@0: tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC; michael@0: /* Check for nsec overflow - otherwise we'll get an EINVAL */ michael@0: if (tt.tv_nsec >= PR_NSEC_PER_SEC) { michael@0: tt.tv_sec++; michael@0: tt.tv_nsec -= PR_NSEC_PER_SEC; michael@0: } michael@0: me->md.sp = unixware_getsp(); michael@0: michael@0: michael@0: /* XXX Solaris 2.5.x gives back EINTR occasionally for no reason michael@0: * hence ignore EINTR for now */ michael@0: michael@0: COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt); michael@0: } michael@0: michael@0: void _MD_lock(struct _MDLock *md_lock) michael@0: { michael@0: mutex_lock(&md_lock->lock); michael@0: } michael@0: michael@0: void _MD_unlock(struct _MDLock *md_lock) michael@0: { michael@0: mutex_unlock(&((md_lock)->lock)); michael@0: } michael@0: michael@0: michael@0: PRThread *_pr_current_thread_tls() michael@0: { michael@0: PRThread *ret; michael@0: michael@0: thr_getspecific(threadid_key, (void **)&ret); michael@0: return ret; michael@0: } michael@0: michael@0: PRStatus michael@0: _MD_WAIT(PRThread *thread, PRIntervalTime ticks) michael@0: { michael@0: _MD_WAIT_SEM(&thread->md.waiter_sem); michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRStatus michael@0: _MD_WAKEUP_WAITER(PRThread *thread) michael@0: { michael@0: if (thread == NULL) { michael@0: return PR_SUCCESS; michael@0: } michael@0: _MD_POST_SEM(&thread->md.waiter_sem); michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: _PRCPU *_pr_current_cpu_tls() michael@0: { michael@0: _PRCPU *ret; michael@0: michael@0: thr_getspecific(cpuid_key, (void **)&ret); michael@0: return ret; michael@0: } michael@0: michael@0: PRThread *_pr_last_thread_tls() michael@0: { michael@0: PRThread *ret; michael@0: michael@0: thr_getspecific(last_thread_key, (void **)&ret); michael@0: return ret; michael@0: } michael@0: michael@0: _MDLock _pr_ioq_lock; michael@0: michael@0: void _MD_INIT_IO (void) michael@0: { michael@0: _MD_NEW_LOCK(&_pr_ioq_lock); michael@0: } michael@0: michael@0: PRStatus _MD_InitializeThread(PRThread *thread) michael@0: { michael@0: if (!_PR_IS_NATIVE_THREAD(thread)) michael@0: return; michael@0: /* prime the sp; substract 4 so we don't hit the assert that michael@0: * curr sp > base_stack michael@0: */ michael@0: thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long); michael@0: thread->md.lwpid = _lwp_self(); michael@0: thread->md.handle = THR_SELF(); michael@0: michael@0: /* all threads on Solaris are global threads from NSPR's perspective michael@0: * since all of them are mapped to Solaris threads. michael@0: */ michael@0: thread->flags |= _PR_GLOBAL_SCOPE; michael@0: michael@0: /* For primordial/attached thread, we don't create an underlying native thread. michael@0: * So, _MD_CREATE_THREAD() does not get called. We need to do initialization michael@0: * like allocating thread's synchronization variables and set the underlying michael@0: * native thread's priority. michael@0: */ michael@0: if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { michael@0: _MD_NEW_SEM(&thread->md.waiter_sem, 0); michael@0: _MD_SET_PRIORITY(&(thread->md), thread->priority); michael@0: } michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: static sigset_t old_mask; /* store away original gc thread sigmask */ michael@0: static int gcprio; /* store away original gc thread priority */ michael@0: static lwpid_t *all_lwps=NULL; /* list of lwps that we suspended */ michael@0: static int num_lwps ; michael@0: static int suspendAllOn = 0; michael@0: michael@0: #define VALID_SP(sp, bottom, top) \ michael@0: (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top))) michael@0: michael@0: void unixware_preempt_off() michael@0: { michael@0: sigset_t set; michael@0: (void)sigfillset(&set); michael@0: sigprocmask (SIG_SETMASK, &set, &old_mask); michael@0: } michael@0: michael@0: void unixware_preempt_on() michael@0: { michael@0: sigprocmask (SIG_SETMASK, &old_mask, NULL); michael@0: } michael@0: michael@0: void _MD_Begin_SuspendAll() michael@0: { michael@0: unixware_preempt_off(); michael@0: michael@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n")); michael@0: /* run at highest prio so I cannot be preempted */ michael@0: thr_getprio(thr_self(), &gcprio); michael@0: thr_setprio(thr_self(), 0x7fffffff); michael@0: suspendAllOn = 1; michael@0: } michael@0: michael@0: void _MD_End_SuspendAll() michael@0: { michael@0: } michael@0: michael@0: void _MD_End_ResumeAll() michael@0: { michael@0: PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n")); michael@0: thr_setprio(thr_self(), gcprio); michael@0: unixware_preempt_on(); michael@0: suspendAllOn = 0; michael@0: } michael@0: michael@0: void _MD_Suspend(PRThread *thr) michael@0: { michael@0: int lwp_fd, result; michael@0: int lwp_main_proc_fd = 0; michael@0: michael@0: thr_suspend(thr->md.handle); michael@0: if (!_PR_IS_GCABLE_THREAD(thr)) michael@0: return; michael@0: /* XXX Primordial thread can't be bound to an lwp, hence there is no michael@0: * way we can assume that we can get the lwp status for primordial michael@0: * thread reliably. Hence we skip this for primordial thread, hoping michael@0: * that the SP is saved during lock and cond. wait. michael@0: * XXX - Again this is concern only for java interpreter, not for the michael@0: * server, 'cause primordial thread in the server does not do java work michael@0: */ michael@0: if (thr->flags & _PR_PRIMORDIAL) michael@0: return; michael@0: michael@0: /* if the thread is not started yet then don't do anything */ michael@0: if (!suspendAllOn || thr->md.lwpid == -1) michael@0: return; michael@0: michael@0: } michael@0: void _MD_Resume(PRThread *thr) michael@0: { michael@0: if (!_PR_IS_GCABLE_THREAD(thr) || !suspendAllOn){ michael@0: /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend michael@0: * during that time we can't call any thread lib or libc calls. Hence michael@0: * make sure that no resume is requested for Non gcable thread michael@0: * during suspendAllOn */ michael@0: PR_ASSERT(!suspendAllOn); michael@0: thr_continue(thr->md.handle); michael@0: return; michael@0: } michael@0: if (thr->md.lwpid == -1) michael@0: return; michael@0: michael@0: if ( _lwp_continue(thr->md.lwpid) < 0) { michael@0: PR_ASSERT(0); /* ARGH, we are hosed! */ michael@0: } michael@0: } michael@0: michael@0: michael@0: PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) michael@0: { michael@0: if (isCurrent) { michael@0: (void) getcontext(CONTEXT(t)); /* XXX tune me: set md_IRIX.c */ michael@0: } michael@0: *np = NGREG; michael@0: if (t->md.lwpid == -1) michael@0: memset(&t->md.context.uc_mcontext.gregs[0], 0, NGREG * sizeof(PRWord)); michael@0: return (PRWord*) &t->md.context.uc_mcontext.gregs[0]; michael@0: } michael@0: michael@0: int michael@0: _pr_unixware_clock_gettime (struct timespec *tp) michael@0: { michael@0: struct timeval tv; michael@0: michael@0: gettimeofday(&tv, NULL); michael@0: tp->tv_sec = tv.tv_sec; michael@0: tp->tv_nsec = tv.tv_usec * 1000; michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: #endif /* USE_SVR4_THREADS */