560 lines
13 KiB
C
560 lines
13 KiB
C
/* -*- 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 <setjmp.h>
|
|
|
|
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 <stdio.h>
|
|
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 <thread.h>
|
|
#include <synch.h>
|
|
|
|
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 <signal.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/lwp.h>
|
|
#include <sys/procfs.h>
|
|
#include <sys/syscall.h>
|
|
|
|
|
|
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 */
|