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