mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-05-03 14:56:17 +00:00
890 lines
23 KiB
C
890 lines
23 KiB
C
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is the Netscape Portable Runtime (NSPR).
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1998-2000
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
#include "primpl.h"
|
||
|
|
||
|
|
||
|
extern PRBool suspendAllOn;
|
||
|
extern PRThread *suspendAllThread;
|
||
|
|
||
|
extern void _MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri);
|
||
|
|
||
|
PRIntervalTime _MD_Solaris_TicksPerSecond(void)
|
||
|
{
|
||
|
/*
|
||
|
* Ticks have a 10-microsecond resolution. So there are
|
||
|
* 100000 ticks per second.
|
||
|
*/
|
||
|
return 100000UL;
|
||
|
}
|
||
|
|
||
|
/* Interval timers, implemented using gethrtime() */
|
||
|
|
||
|
PRIntervalTime _MD_Solaris_GetInterval(void)
|
||
|
{
|
||
|
union {
|
||
|
hrtime_t hrt; /* hrtime_t is a 64-bit (long long) integer */
|
||
|
PRInt64 pr64;
|
||
|
} time;
|
||
|
PRInt64 resolution;
|
||
|
PRIntervalTime ticks;
|
||
|
|
||
|
time.hrt = gethrtime(); /* in nanoseconds */
|
||
|
/*
|
||
|
* Convert from nanoseconds to ticks. A tick's resolution is
|
||
|
* 10 microseconds, or 10000 nanoseconds.
|
||
|
*/
|
||
|
LL_I2L(resolution, 10000);
|
||
|
LL_DIV(time.pr64, time.pr64, resolution);
|
||
|
LL_L2UI(ticks, time.pr64);
|
||
|
return ticks;
|
||
|
}
|
||
|
|
||
|
#ifdef _PR_PTHREADS
|
||
|
void _MD_EarlyInit(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
|
||
|
{
|
||
|
*np = 0;
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif /* _PR_PTHREADS */
|
||
|
|
||
|
#if !defined(i386) && !defined(IS_64)
|
||
|
#if defined(_PR_HAVE_ATOMIC_OPS)
|
||
|
/* 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.
|
||
|
*
|
||
|
* This code uses the Solaris threads API. It can be used in both the
|
||
|
* pthreads and Solaris threads versions of nspr20 because "POSIX threads
|
||
|
* and Solaris threads are fully compatible even within the same process",
|
||
|
* to quote from pthread_create(3T).
|
||
|
*/
|
||
|
|
||
|
#include <thread.h>
|
||
|
#include <synch.h>
|
||
|
|
||
|
static mutex_t _solaris_atomic = DEFAULTMUTEX;
|
||
|
|
||
|
PRInt32
|
||
|
_MD_AtomicIncrement(PRInt32 *val)
|
||
|
{
|
||
|
PRInt32 rv;
|
||
|
if (mutex_lock(&_solaris_atomic) != 0)
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
rv = ++(*val);
|
||
|
|
||
|
if (mutex_unlock(&_solaris_atomic) != 0)\
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
PRInt32
|
||
|
_MD_AtomicAdd(PRInt32 *ptr, PRInt32 val)
|
||
|
{
|
||
|
PRInt32 rv;
|
||
|
if (mutex_lock(&_solaris_atomic) != 0)
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
rv = ((*ptr) += val);
|
||
|
|
||
|
if (mutex_unlock(&_solaris_atomic) != 0)\
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
PRInt32
|
||
|
_MD_AtomicDecrement(PRInt32 *val)
|
||
|
{
|
||
|
PRInt32 rv;
|
||
|
if (mutex_lock(&_solaris_atomic) != 0)
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
rv = --(*val);
|
||
|
|
||
|
if (mutex_unlock(&_solaris_atomic) != 0)\
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
PRInt32
|
||
|
_MD_AtomicSet(PRInt32 *val, PRInt32 newval)
|
||
|
{
|
||
|
PRInt32 rv;
|
||
|
if (mutex_lock(&_solaris_atomic) != 0)
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
rv = *val;
|
||
|
*val = newval;
|
||
|
|
||
|
if (mutex_unlock(&_solaris_atomic) != 0)\
|
||
|
PR_ASSERT(0);
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
#endif /* _PR_HAVE_ATOMIC_OPS */
|
||
|
#endif /* !defined(i386) */
|
||
|
|
||
|
#if defined(_PR_GLOBAL_THREADS_ONLY)
|
||
|
#include <signal.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <thread.h>
|
||
|
|
||
|
#include <sys/lwp.h>
|
||
|
#include <sys/procfs.h>
|
||
|
#include <sys/syscall.h>
|
||
|
extern int syscall(); /* not declared in sys/syscall.h */
|
||
|
|
||
|
static sigset_t old_mask; /* store away original gc thread sigmask */
|
||
|
static PRIntn gcprio; /* store away original gc thread priority */
|
||
|
|
||
|
THREAD_KEY_T threadid_key;
|
||
|
THREAD_KEY_T cpuid_key;
|
||
|
THREAD_KEY_T last_thread_key;
|
||
|
static sigset_t set, oldset;
|
||
|
|
||
|
static void
|
||
|
threadid_key_destructor(void *value)
|
||
|
{
|
||
|
PRThread *me = (PRThread *)value;
|
||
|
PR_ASSERT(me != NULL);
|
||
|
/* the thread could be PRIMORDIAL (thus not ATTACHED) */
|
||
|
if (me->flags & _PR_ATTACHED) {
|
||
|
/*
|
||
|
* The Solaris thread library sets the thread specific
|
||
|
* data (the current thread) to NULL before invoking
|
||
|
* the destructor. We need to restore it to prevent the
|
||
|
* _PR_MD_CURRENT_THREAD() call in _PRI_DetachThread()
|
||
|
* from attaching the thread again.
|
||
|
*/
|
||
|
_PR_MD_SET_CURRENT_THREAD(me);
|
||
|
_PRI_DetachThread();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void _MD_EarlyInit(void)
|
||
|
{
|
||
|
THR_KEYCREATE(&threadid_key, threadid_key_destructor);
|
||
|
THR_KEYCREATE(&cpuid_key, NULL);
|
||
|
THR_KEYCREATE(&last_thread_key, NULL);
|
||
|
sigemptyset(&set);
|
||
|
sigaddset(&set, SIGALRM);
|
||
|
}
|
||
|
|
||
|
PRStatus _MD_CreateThread(PRThread *thread,
|
||
|
void (*start)(void *),
|
||
|
PRThreadPriority priority,
|
||
|
PRThreadScope scope,
|
||
|
PRThreadState state,
|
||
|
PRUint32 stackSize)
|
||
|
{
|
||
|
PRInt32 flags;
|
||
|
|
||
|
/* mask out SIGALRM for native thread creation */
|
||
|
thr_sigsetmask(SIG_BLOCK, &set, &oldset);
|
||
|
|
||
|
/*
|
||
|
* Note that we create joinable threads with the THR_DETACHED
|
||
|
* flag. The reasons why we don't use thr_join to implement
|
||
|
* PR_JoinThread are:
|
||
|
* - We use a termination condition variable in the PRThread
|
||
|
* structure to implement PR_JoinThread across all classic
|
||
|
* nspr implementation strategies.
|
||
|
* - The native threads may be recycled by NSPR to run other
|
||
|
* new NSPR threads, so the native threads may not terminate
|
||
|
* when the corresponding NSPR threads terminate.
|
||
|
*/
|
||
|
flags = THR_SUSPENDED|THR_DETACHED;
|
||
|
if (_PR_IS_GCABLE_THREAD(thread) || (thread->flags & _PR_BOUND_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;
|
||
|
}
|
||
|
|
||
|
_MD_SET_PRIORITY(&(thread->md), 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;
|
||
|
|
||
|
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_exit_thread(PRThread *thread)
|
||
|
{
|
||
|
_MD_CLEAN_THREAD(thread);
|
||
|
_MD_SET_CURRENT_THREAD(NULL);
|
||
|
}
|
||
|
|
||
|
void _MD_SET_PRIORITY(_MDThread *md_thread,
|
||
|
PRThreadPriority newPri)
|
||
|
{
|
||
|
PRIntn nativePri;
|
||
|
|
||
|
if (newPri < PR_PRIORITY_FIRST) {
|
||
|
newPri = PR_PRIORITY_FIRST;
|
||
|
} else if (newPri > PR_PRIORITY_LAST) {
|
||
|
newPri = PR_PRIORITY_LAST;
|
||
|
}
|
||
|
/* Solaris priorities are from 0 to 127 */
|
||
|
nativePri = newPri * 127 / PR_PRIORITY_LAST;
|
||
|
if(thr_setprio((thread_t)md_thread->handle, nativePri)) {
|
||
|
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;
|
||
|
PRThread *me = _PR_MD_CURRENT_THREAD();
|
||
|
|
||
|
PR_ASSERT((!suspendAllOn) || (suspendAllThread != me));
|
||
|
|
||
|
if (PR_INTERVAL_NO_TIMEOUT == timeout) {
|
||
|
COND_WAIT(&md_cv->cv, &md_lock->lock);
|
||
|
} else {
|
||
|
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;
|
||
|
}
|
||
|
COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void _MD_lock(struct _MDLock *md_lock)
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
/* This code was used for GC testing to make sure that we didn't attempt
|
||
|
* to grab any locks while threads are suspended.
|
||
|
*/
|
||
|
PRLock *lock;
|
||
|
|
||
|
if ((suspendAllOn) && (suspendAllThread == _PR_MD_CURRENT_THREAD())) {
|
||
|
lock = ((PRLock *) ((char*) (md_lock) - offsetof(PRLock,ilock)));
|
||
|
PR_ASSERT(lock->owner == NULL);
|
||
|
return;
|
||
|
}
|
||
|
#endif /* DEBUG */
|
||
|
|
||
|
mutex_lock(&md_lock->lock);
|
||
|
}
|
||
|
|
||
|
PRThread *_pr_attached_thread_tls()
|
||
|
{
|
||
|
PRThread *ret;
|
||
|
|
||
|
thr_getspecific(threadid_key, (void **)&ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
PRThread *_pr_current_thread_tls()
|
||
|
{
|
||
|
PRThread *thread;
|
||
|
|
||
|
thread = _MD_GET_ATTACHED_THREAD();
|
||
|
|
||
|
if (NULL == thread) {
|
||
|
thread = _PRI_AttachThread(
|
||
|
PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
|
||
|
}
|
||
|
PR_ASSERT(thread != NULL);
|
||
|
|
||
|
return thread;
|
||
|
}
|
||
|
|
||
|
PRStatus
|
||
|
_MD_wait(PRThread *thread, PRIntervalTime ticks)
|
||
|
{
|
||
|
_MD_WAIT_SEM(&thread->md.waiter_sem);
|
||
|
return PR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
PRStatus
|
||
|
_MD_WakeupWaiter(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_InitIO(void)
|
||
|
{
|
||
|
_MD_NEW_LOCK(&_pr_ioq_lock);
|
||
|
}
|
||
|
|
||
|
PRStatus _MD_InitializeThread(PRThread *thread)
|
||
|
{
|
||
|
if (!_PR_IS_NATIVE_THREAD(thread))
|
||
|
return PR_SUCCESS;
|
||
|
/* sol_curthread is an asm routine which grabs GR7; GR7 stores an internal
|
||
|
* thread structure ptr used by solaris. We'll use this ptr later
|
||
|
* with suspend/resume to find which threads are running on LWPs.
|
||
|
*/
|
||
|
thread->md.threadID = sol_curthread();
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* Sleep for n milliseconds, n < 1000 */
|
||
|
void solaris_msec_sleep(int n)
|
||
|
{
|
||
|
struct timespec ts;
|
||
|
|
||
|
ts.tv_sec = 0;
|
||
|
ts.tv_nsec = 1000000*n;
|
||
|
if (syscall(SYS_nanosleep, &ts, 0, 0) < 0) {
|
||
|
PR_ASSERT(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define VALID_SP(sp, bottom, top) \
|
||
|
(((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top)))
|
||
|
|
||
|
void solaris_record_regs(PRThread *t, prstatus_t *lwpstatus)
|
||
|
{
|
||
|
#ifdef sparc
|
||
|
long *regs = (long *)&t->md.context.uc_mcontext.gregs[0];
|
||
|
|
||
|
PR_ASSERT(_PR_IS_GCABLE_THREAD(t));
|
||
|
PR_ASSERT(t->md.threadID == lwpstatus->pr_reg[REG_G7]);
|
||
|
|
||
|
t->md.sp = lwpstatus->pr_reg[REG_SP];
|
||
|
PR_ASSERT(VALID_SP(t->md.sp, t->stack->stackBottom, t->stack->stackTop));
|
||
|
|
||
|
regs[0] = lwpstatus->pr_reg[R_G1];
|
||
|
regs[1] = lwpstatus->pr_reg[R_G2];
|
||
|
regs[2] = lwpstatus->pr_reg[R_G3];
|
||
|
regs[3] = lwpstatus->pr_reg[R_G4];
|
||
|
regs[4] = lwpstatus->pr_reg[R_O0];
|
||
|
regs[5] = lwpstatus->pr_reg[R_O1];
|
||
|
regs[6] = lwpstatus->pr_reg[R_O2];
|
||
|
regs[7] = lwpstatus->pr_reg[R_O3];
|
||
|
regs[8] = lwpstatus->pr_reg[R_O4];
|
||
|
regs[9] = lwpstatus->pr_reg[R_O5];
|
||
|
regs[10] = lwpstatus->pr_reg[R_O6];
|
||
|
regs[11] = lwpstatus->pr_reg[R_O7];
|
||
|
#elif defined(i386)
|
||
|
/*
|
||
|
* To be implemented and tested
|
||
|
*/
|
||
|
PR_ASSERT(0);
|
||
|
PR_ASSERT(t->md.threadID == lwpstatus->pr_reg[GS]);
|
||
|
t->md.sp = lwpstatus->pr_reg[UESP];
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void solaris_preempt_off()
|
||
|
{
|
||
|
sigset_t set;
|
||
|
|
||
|
(void)sigfillset(&set);
|
||
|
syscall(SYS_sigprocmask, SIG_SETMASK, &set, &old_mask);
|
||
|
}
|
||
|
|
||
|
void solaris_preempt_on()
|
||
|
{
|
||
|
syscall(SYS_sigprocmask, SIG_SETMASK, &old_mask, NULL);
|
||
|
}
|
||
|
|
||
|
int solaris_open_main_proc_fd()
|
||
|
{
|
||
|
char buf[30];
|
||
|
int fd;
|
||
|
|
||
|
/* Not locked, so must be created while threads coming up */
|
||
|
PR_snprintf(buf, sizeof(buf), "/proc/%ld", getpid());
|
||
|
if ( (fd = syscall(SYS_open, buf, O_RDONLY)) < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
return fd;
|
||
|
}
|
||
|
|
||
|
/* Return a file descriptor for the /proc entry corresponding to the
|
||
|
* given lwp.
|
||
|
*/
|
||
|
int solaris_open_lwp(lwpid_t id, int lwp_main_proc_fd)
|
||
|
{
|
||
|
int result;
|
||
|
|
||
|
if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCOPENLWP, &id)) <0)
|
||
|
return -1; /* exited??? */
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
void _MD_Begin_SuspendAll()
|
||
|
{
|
||
|
solaris_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 = PR_TRUE;
|
||
|
suspendAllThread = _PR_MD_CURRENT_THREAD();
|
||
|
}
|
||
|
|
||
|
void _MD_End_SuspendAll()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void _MD_End_ResumeAll()
|
||
|
{
|
||
|
PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n"));
|
||
|
thr_setprio(thr_self(), gcprio);
|
||
|
solaris_preempt_on();
|
||
|
suspendAllThread = NULL;
|
||
|
suspendAllOn = PR_FALSE;
|
||
|
}
|
||
|
|
||
|
void _MD_Suspend(PRThread *thr)
|
||
|
{
|
||
|
int lwp_fd, result;
|
||
|
prstatus_t lwpstatus;
|
||
|
int lwp_main_proc_fd = 0;
|
||
|
|
||
|
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 suspension is requested for Non gcable thread
|
||
|
* during suspendAllOn */
|
||
|
PR_ASSERT(!suspendAllOn);
|
||
|
thr_suspend(thr->md.handle);
|
||
|
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;
|
||
|
|
||
|
/* XXX Important Note: If the start function of a thread is not called,
|
||
|
* lwpid is -1. Then, skip this thread. This thread will get caught
|
||
|
* in PR_NativeRunThread before calling the start function, because
|
||
|
* we hold the pr_activeLock during suspend/resume */
|
||
|
|
||
|
/* if the thread is not started yet then don't do anything */
|
||
|
if (!suspendAllOn || thr->md.lwpid == -1)
|
||
|
return;
|
||
|
|
||
|
if (_lwp_suspend(thr->md.lwpid) < 0) {
|
||
|
PR_ASSERT(0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( (lwp_main_proc_fd = solaris_open_main_proc_fd()) < 0) {
|
||
|
PR_ASSERT(0);
|
||
|
return; /* XXXMB ARGH, we're hosed! */
|
||
|
}
|
||
|
|
||
|
if ( (lwp_fd = solaris_open_lwp(thr->md.lwpid, lwp_main_proc_fd)) < 0) {
|
||
|
PR_ASSERT(0);
|
||
|
close(lwp_main_proc_fd);
|
||
|
return;
|
||
|
}
|
||
|
if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
|
||
|
/* Hopefully the thread just died... */
|
||
|
close(lwp_fd);
|
||
|
close(lwp_main_proc_fd);
|
||
|
return;
|
||
|
}
|
||
|
while ( !(lwpstatus.pr_flags & PR_STOPPED) ) {
|
||
|
if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
|
||
|
PR_ASSERT(0); /* ARGH SOMETHING WRONG! */
|
||
|
break;
|
||
|
}
|
||
|
solaris_msec_sleep(1);
|
||
|
}
|
||
|
solaris_record_regs(thr, &lwpstatus);
|
||
|
close(lwp_fd);
|
||
|
close(lwp_main_proc_fd);
|
||
|
}
|
||
|
|
||
|
#ifdef OLD_CODE
|
||
|
|
||
|
void _MD_SuspendAll()
|
||
|
{
|
||
|
/* On solaris there are threads, and there are LWPs.
|
||
|
* Calling _PR_DoSingleThread would freeze all of the threads bound to LWPs
|
||
|
* but not necessarily stop all LWPs (for example if someone did
|
||
|
* an attachthread of a thread which was not bound to an LWP).
|
||
|
* So now go through all the LWPs for this process and freeze them.
|
||
|
*
|
||
|
* Note that if any thread which is capable of having the GC run on it must
|
||
|
* had better be a LWP with a single bound thread on it. Otherwise, this
|
||
|
* might not stop that thread from being run.
|
||
|
*/
|
||
|
PRThread *current = _PR_MD_CURRENT_THREAD();
|
||
|
prstatus_t status, lwpstatus;
|
||
|
int result, index, lwp_fd;
|
||
|
lwpid_t me = _lwp_self();
|
||
|
int err;
|
||
|
int lwp_main_proc_fd;
|
||
|
|
||
|
solaris_preempt_off();
|
||
|
|
||
|
/* run at highest prio so I cannot be preempted */
|
||
|
thr_getprio(thr_self(), &gcprio);
|
||
|
thr_setprio(thr_self(), 0x7fffffff);
|
||
|
|
||
|
current->md.sp = (uint_t)&me; /* set my own stack pointer */
|
||
|
|
||
|
if ( (lwp_main_proc_fd = solaris_open_main_proc_fd()) < 0) {
|
||
|
PR_ASSERT(0);
|
||
|
solaris_preempt_on();
|
||
|
return; /* XXXMB ARGH, we're hosed! */
|
||
|
}
|
||
|
|
||
|
if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCSTATUS, &status)) < 0) {
|
||
|
err = errno;
|
||
|
PR_ASSERT(0);
|
||
|
goto failure; /* XXXMB ARGH, we're hosed! */
|
||
|
}
|
||
|
|
||
|
num_lwps = status.pr_nlwp;
|
||
|
|
||
|
if ( (all_lwps = (lwpid_t *)PR_MALLOC((num_lwps+1) * sizeof(lwpid_t)))==NULL) {
|
||
|
PR_ASSERT(0);
|
||
|
goto failure; /* XXXMB ARGH, we're hosed! */
|
||
|
}
|
||
|
|
||
|
if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCLWPIDS, all_lwps)) < 0) {
|
||
|
PR_ASSERT(0);
|
||
|
PR_DELETE(all_lwps);
|
||
|
goto failure; /* XXXMB ARGH, we're hosed! */
|
||
|
}
|
||
|
|
||
|
for (index=0; index< num_lwps; index++) {
|
||
|
if (all_lwps[index] != me) {
|
||
|
if (_lwp_suspend(all_lwps[index]) < 0) {
|
||
|
/* could happen if lwp exited */
|
||
|
all_lwps[index] = me; /* dummy it up */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Turns out that lwp_suspend is not a blocking call.
|
||
|
* Go through the list and make sure they are all stopped.
|
||
|
*/
|
||
|
for (index=0; index< num_lwps; index++) {
|
||
|
if (all_lwps[index] != me) {
|
||
|
if ( (lwp_fd = solaris_open_lwp(all_lwps[index], lwp_main_proc_fd)) < 0) {
|
||
|
PR_ASSERT(0);
|
||
|
PR_DELETE(all_lwps);
|
||
|
all_lwps = NULL;
|
||
|
goto failure; /* XXXMB ARGH, we're hosed! */
|
||
|
}
|
||
|
|
||
|
if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
|
||
|
/* Hopefully the thread just died... */
|
||
|
close(lwp_fd);
|
||
|
continue;
|
||
|
}
|
||
|
while ( !(lwpstatus.pr_flags & PR_STOPPED) ) {
|
||
|
if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
|
||
|
PR_ASSERT(0); /* ARGH SOMETHING WRONG! */
|
||
|
break;
|
||
|
}
|
||
|
solaris_msec_sleep(1);
|
||
|
}
|
||
|
solaris_record_regs(&lwpstatus);
|
||
|
close(lwp_fd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
close(lwp_main_proc_fd);
|
||
|
|
||
|
return;
|
||
|
failure:
|
||
|
solaris_preempt_on();
|
||
|
thr_setprio(thr_self(), gcprio);
|
||
|
close(lwp_main_proc_fd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void _MD_ResumeAll()
|
||
|
{
|
||
|
int i;
|
||
|
lwpid_t me = _lwp_self();
|
||
|
|
||
|
for (i=0; i < num_lwps; i++) {
|
||
|
if (all_lwps[i] == me)
|
||
|
continue;
|
||
|
if ( _lwp_continue(all_lwps[i]) < 0) {
|
||
|
PR_ASSERT(0); /* ARGH, we are hosed! */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* restore priority and sigmask */
|
||
|
thr_setprio(thr_self(), gcprio);
|
||
|
solaris_preempt_on();
|
||
|
PR_DELETE(all_lwps);
|
||
|
all_lwps = NULL;
|
||
|
}
|
||
|
#endif /* OLD_CODE */
|
||
|
|
||
|
#ifdef USE_SETJMP
|
||
|
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);
|
||
|
}
|
||
|
#else
|
||
|
PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
|
||
|
{
|
||
|
if (isCurrent) {
|
||
|
(void) getcontext(CONTEXT(t));
|
||
|
}
|
||
|
*np = NGREG;
|
||
|
return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
|
||
|
}
|
||
|
#endif /* USE_SETJMP */
|
||
|
|
||
|
#else /* _PR_GLOBAL_THREADS_ONLY */
|
||
|
|
||
|
#if defined(_PR_LOCAL_THREADS_ONLY)
|
||
|
|
||
|
void _MD_EarlyInit(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void _MD_SolarisInit()
|
||
|
{
|
||
|
_PR_UnixInit();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority 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)
|
||
|
{
|
||
|
PR_ASSERT((thread == NULL) || (!(thread->flags & _PR_GLOBAL_SCOPE)));
|
||
|
return PR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* These functions should not be called for Solaris */
|
||
|
void
|
||
|
_MD_YIELD(void)
|
||
|
{
|
||
|
PR_NOT_REACHED("_MD_YIELD should not be called for Solaris");
|
||
|
}
|
||
|
|
||
|
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 Solaris");
|
||
|
return(PR_FAILURE);
|
||
|
}
|
||
|
|
||
|
#ifdef USE_SETJMP
|
||
|
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);
|
||
|
}
|
||
|
#else
|
||
|
PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
|
||
|
{
|
||
|
if (isCurrent) {
|
||
|
(void) getcontext(CONTEXT(t));
|
||
|
}
|
||
|
*np = NGREG;
|
||
|
return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
|
||
|
}
|
||
|
#endif /* USE_SETJMP */
|
||
|
|
||
|
#endif /* _PR_LOCAL_THREADS_ONLY */
|
||
|
|
||
|
#endif /* _PR_GLOBAL_THREADS_ONLY */
|
||
|
|
||
|
#ifndef _PR_PTHREADS
|
||
|
#if defined(i386) && defined(SOLARIS2_4)
|
||
|
/*
|
||
|
* Because clock_gettime() on Solaris/x86 2.4 always generates a
|
||
|
* segmentation fault, we use an emulated version _pr_solx86_clock_gettime(),
|
||
|
* which is implemented using gettimeofday().
|
||
|
*/
|
||
|
|
||
|
int
|
||
|
_pr_solx86_clock_gettime(clockid_t clock_id, struct timespec *tp)
|
||
|
{
|
||
|
struct timeval tv;
|
||
|
|
||
|
if (clock_id != CLOCK_REALTIME) {
|
||
|
errno = EINVAL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
gettimeofday(&tv, NULL);
|
||
|
tp->tv_sec = tv.tv_sec;
|
||
|
tp->tv_nsec = tv.tv_usec * 1000;
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* i386 && SOLARIS2_4 */
|
||
|
#endif /* _PR_PTHREADS */
|