netscape-revival
nspr/src/hwthread.c
#ifdef HW_THREADS
#include <prmem.h>
#include <prlog.h>
#include <prdump.h>
#include <prfile.h>
#include "hwkern.h"
#include "mdint.h"
PR_LOG_DEFINE(THREAD);
/*
** Software threading package. Designed to run on top of MULTIPLE native
** OS threads.
*/
PR_PUBLIC_API(DWORD) PR_GetSP( PRThread * thread )
{
CONTEXT context;
DWORD thread_sp = 0;
int count = 0;
again:
context.ContextFlags = CONTEXT_CONTROL;
if( GetThreadContext( thread->handle, &context ) ) {
thread_sp = context.Esp;
}
/*
** This check is necessary for Win95 which sometimes leaves a thread
** in a state where its esp IS NOT valid! If this happens, try it
** a few more times... If esp is still bogus, then return the max
** valid stack. This will keep the GC from getting corrupted.
*/
if( (thread_sp > (DWORD)thread->stack->stackTop) ||
thread_sp < (DWORD)(thread->stack->stackBottom) ) {
PR_LOG(THREAD, out,
("SP is bogus: %x. Trying again!", thread_sp));
if( ++count < 50 ) {
Sleep(0);
goto again;
} else {
thread_sp = (DWORD)((prword_t *)thread->stack->stackTop - 0x2000);
/* thread_sp = (DWORD)((prword_t *)thread->stack->stackBottom + 1); */
}
}
return thread_sp;
}
PR_PUBLIC_API(PRThread *) PR_CreateThread(char *name, int priority,
size_t stackSize)
{
PRThread *t;
t = (PRThread*) calloc(1, sizeof(PRThread));
if (t) {
PR_INIT_CLIST(&t->monitors);
PR_INIT_CLIST(&t->runqLinks);
PR_INIT_CLIST(&t->waitLinks);
t->state = _PR_UNBORN;
t->priority = priority;
/* Set up the thread stack information */
if( !_MD_NewStack(t, stackSize) ) {
free(t);
t = 0;
goto done;
}
/* Copy the name of the thread */
t->name = strdup(name);
if( !t->name ) {
free(t);
t = 0;
goto done;
}
/* Create the Event object used by condition variables */
t->condEvent = CreateEvent(NULL, FALSE, FALSE, NULL );
if (!t->condEvent) {
free(t->name);
free(t);
t = 0;
goto done;
}
}
done:
return t;
}
PR_PUBLIC_API(void) PR_DestroyThread(PRThread *thread)
{
PRPerThreadData **pptd, *ptd;
HANDLE hdl;
PRMonitor *mon;
LOCK_SCHEDULER();
hdl = thread->handle;
/*
** First, suspend the thread (unless it's the active one)
*/
if ( thread != _pr_current_thread ) {
#if defined( XP_PC ) && defined( _WIN32 )
SuspendThread( hdl );
#endif /* XP_PC && _WIN32 */
}
PR_LOG(THREAD, out,
("%s(0X%x)[DestroyThread]", thread->name, thread));
PR_REMOVE_LINK(&thread->runqLinks); /* take off _pr_runq */
/*
** Release any monitors held by the thread...
*/
while (thread->monitors.next != &thread->monitors) {
mon = MONITOR_PTR(thread->monitors.next);
PR_REMOVE_AND_INIT_LINK(&mon->links);
mon->owner = 0;
mon->count = 0;
/* release the monitor */
PR_LOG(THREAD, out,
("%s(0X%x)[Releasing Monitor] %s", thread->name, thread,
mon->name));
LeaveCriticalSection( &(mon->mutexHandle) );
}
if (thread->flags & _PR_SYSTEM) {
_pr_system_active--;
PR_ASSERT(_pr_system_active >= 0);
} else {
_pr_user_active--;
PR_ASSERT(_pr_user_active >= 0);
}
/* Free up memory and we're gone... */
_MD_FreeStack(thread);
/* XXX: this assumes that ptd->priv is GC'able memory or static */
pptd = &thread->ptd;
while ((ptd = *pptd) != 0) {
*pptd = ptd->next;
free(ptd);
}
/* Free up the name */
if (thread->name) {
free(thread->name);
thread->name = 0;
}
/* Free the Event object used by condition variables */
if (thread->condEvent) {
CloseHandle(thread->condEvent);
}
/* Now we can free up the thread object */
free( thread );
if (_pr_user_active == 0) {
UNLOCK_SCHEDULER();
exit(0);
}
UNLOCK_SCHEDULER();
#if defined( XP_PC ) && defined( _WIN32 )
if ( thread != _pr_current_thread ) {
TerminateThread( hdl, 0L );
} else {
CloseHandle(hdl);
ExitThread(0);
}
#endif /* XP_PC && _WIN32 */
}
PR_PUBLIC_API(void) PR_Exit(void)
{
PR_LOG_FLUSH();
PR_DestroyThread( _pr_current_thread );
PR_NOT_REACHED("Continuing after exit.");
}
PR_PUBLIC_API(void) PR_Yield(void)
{
Sleep(0);
}
/*
** Intermediate that invokes startup routine for thread. Hoptoad is executed
** in the context of the new thread...
*/
#ifdef _WIN32
static DWORD Win32_HopToad( PRThread *thread )
{
_pr_current_thread = thread;
/* Set up the thread stack information */
_MD_InitializeStack(thread->stack);
thread->state = _PR_RUNNING;
#if 1
__try {
(thread->asyncCall)(thread->asyncArg1, thread->asyncArg2);
}
__except( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
#ifdef DEBUG
const char *msg = "This thread has caused an access violation. It will be terminated\n";
(void)MessageBox(NULL, msg, "Internal Java Error", MB_OK);
DebugBreak();
#endif /* DEBUG */
PR_Exit();
}
#else
(thread->asyncCall)(thread->asyncArg1, thread->asyncArg2);
#endif
PR_Exit();
PR_NOT_REACHED("Exiting HopToad.");
return 0L;
}
#endif /* _WIN32 */
PR_PUBLIC_API(int) PR_Start(PRThread *thread, void (*e)(void*,void*),
void *o, void *a)
{
int rv;
int pri;
LOCK_SCHEDULER();
rv = -1;
if (thread->state != _PR_UNBORN ||
(thread->priority > 31 || thread->priority < 0)) {
goto done;
}
thread->asyncCall = e;
thread->asyncArg1 = o;
thread->asyncArg2 = a;
/* Initialize and start up the new thread */
#if defined( XP_PC ) && defined( _WIN32 )
thread->handle = CreateThread(
NULL, /* security attrib */
0, /* def. stack size */
(LPTHREAD_START_ROUTINE)Win32_HopToad,/* startup routine */
thread, /* thread param */
CREATE_SUSPENDED, /* create flags */
&(thread->id) ); /* thread id */
if (thread->handle) {
/* Update count of active threads */
if (thread->flags & _PR_SYSTEM) {
_pr_system_active++;
} else {
_pr_user_active++;
}
/*
** 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.
*/
pri = thread->priority;
thread->priority = 100;
PR_SetThreadPriority( thread, pri );
PR_LOG(THREAD, out,
("%s(0X%x)[Start]: on to runq at priority %d",
thread->name, thread, thread->priority));
/* Activate the thread */
if ( ResumeThread( thread->handle ) != 0xFFFFFFFF ) {
rv = 0;
}
}
#endif /* XP_PC && _WIN32 */
done:
UNLOCK_SCHEDULER();
return rv;
}
PR_PUBLIC_API(int) PR_Suspend(PRThread *thread)
{
int rv = 0;
LOCK_SCHEDULER();
PR_LOG(THREAD, out,
("%s(0X%x)[Suspend]: on to runq at priority %d",
thread->name, thread, thread->priority));
switch (thread->state) {
case _PR_UNBORN:
case _PR_ZOMBIE:
case _PR_SUSPENDED:
rv = -1;
break;
case _PR_RUNNING:
thread->state = _PR_SUSPENDED;
/* Release the thread lock if the current thread is suspending */
if( thread == _pr_current_thread ) {
UNLOCK_SCHEDULER();
}
/*
** Suspend the thread...
*/
SuspendThread( thread->handle );
/* Get the thread lock if the current thread just woke up */
if( thread == _pr_current_thread ) {
LOCK_SCHEDULER();
}
break;
case _PR_SLEEPING:
case _PR_COND_WAIT:
case _PR_MON_WAIT:
if( !(thread->flags & _PR_SUSPENDING) ) {
SuspendThread( thread->handle );
thread->flags |= _PR_SUSPENDING;
} else {
rv = -1;
}
break;
}
UNLOCK_SCHEDULER();
return rv;
}
PR_PUBLIC_API(int) PR_Resume(PRThread *thread)
{
int rv = 0;
LOCK_SCHEDULER();
switch (thread->state) {
case _PR_UNBORN:
case _PR_ZOMBIE:
case _PR_RUNNING:
rv = -1;
break;
case _PR_SUSPENDED:
PR_LOG(THREAD, out,
("%s(0X%x)[Resume]: on to runq at priority %d",
thread->name, thread, thread->priority));
thread->state = _PR_RUNNING;
ResumeThread( thread->handle );
break;
case _PR_SLEEPING:
case _PR_COND_WAIT:
case _PR_MON_WAIT:
if (thread->flags & _PR_SUSPENDING) {
PR_LOG(THREAD, out,
("%s(0X%x)[Resume]: Thread is now waiting",
thread->name, thread));
thread->flags &= ~_PR_SUSPENDING;
ResumeThread( thread->handle );
} else {
rv = -1;
}
break;
}
UNLOCK_SCHEDULER();
return rv;
}
PR_PUBLIC_API(int) PR_GetThreadPriority(PRThread *thread)
{
return thread->priority;
}
PR_PUBLIC_API(void) PR_SetThreadPriority(PRThread *thread, int newPri)
{
LOCK_SCHEDULER();
if (newPri < 0) {
newPri = 0;
} else if (newPri >= PR_NUM_PRIORITIES) {
newPri = PR_NUM_PRIORITIES - 1;
}
if (newPri != thread->priority) {
thread->priority = newPri;
/* Move to different _pr_runq */
PR_REMOVE_LINK(&thread->runqLinks);
PR_APPEND_LINK(&thread->runqLinks, &_pr_runq[newPri]);
/* Set the thread priority */
if( newPri < 4 ) {
newPri = THREAD_PRIORITY_IDLE;
} else if( newPri < 8 ) {
newPri = THREAD_PRIORITY_LOWEST;
} else if( newPri < 12 ) {
newPri = THREAD_PRIORITY_BELOW_NORMAL;
} else if( newPri < 16 ) {
newPri = THREAD_PRIORITY_NORMAL;
} else if( newPri < 24 ) {
newPri = THREAD_PRIORITY_ABOVE_NORMAL;
} else if( newPri < 28 ) {
newPri = THREAD_PRIORITY_HIGHEST;
} else if( newPri < 32 ) {
newPri = THREAD_PRIORITY_TIME_CRITICAL;
}
PR_LOG(THREAD, out,
("%s(0X%x)[SetThreadPriority]: on to runq at priority %d",
thread->name, thread, thread->priority));
if( ! SetThreadPriority( thread->handle, newPri ) ) {
PR_LOG(THREAD, out,
("PR_SetThreadPriority: can't set thread priority"));
}
}
UNLOCK_SCHEDULER();
}
PR_PUBLIC_API(int) PR_ActiveCount(void)
{
return _pr_user_active + _pr_system_active;
}
PR_PUBLIC_API(int) PR_EnumerateThreads(PREnumerator func, void *arg)
{
int i, j, rv;
PRCList *qp;
PRThread *t;
LOCK_SCHEDULER();
i = 0;
for (j = 0; j < PR_NUM_PRIORITIES; j++) {
qp = _pr_runq[j].next;
while (qp != &_pr_runq[j]) {
t = THREAD_PTR(qp);
rv = (*func)(t, i, arg);
if (rv == 0) goto done;
i++;
qp = qp->next;
}
}
done:
UNLOCK_SCHEDULER();
return i;
}
/*
** Sleep for an amount of time.
**
** The sleep time is represented in microseconds...
*/
PR_PUBLIC_API(void) PR_Sleep(int64 sleep)
{
PRThread *thread;
unsigned long waitTime;
PR_ASSERT(LL_GE_ZERO(sleep));
thread = PR_CurrentThread();
LOCK_SCHEDULER();
if( !thread->pendingException ) {
PR_LOG(THREAD, warn,
("put %s(0X%x) on sleepQ for %lld",
thread->name, thread, sleep));
thread->flags |= _PR_ON_SLEEPQ;
thread->sleep = sleep;
thread->state = _PR_SLEEPING;
/*
* Convert the sleep time from an int64 in microseconds to
* an int32 in milliseconds
*/
LL_L2UI( waitTime, sleep );
waitTime = waitTime / PR_USEC_PER_MSEC;
UNLOCK_SCHEDULER();
/*
** Put the thread to sleep for the requested time...
*/
#if defined( XP_PC ) && defined( _WIN32 )
WaitForSingleObject( thread->condEvent, waitTime );
#endif /* XP_PC && _WIN32 */
LOCK_SCHEDULER();
/*
** The thread is now awake again...
*/
PR_LOG(THREAD, warn,
("thread=%s(0X%x) done sleeping; state=%d",
thread->name, thread, thread->state));
thread->state = _PR_RUNNING;
}
UNLOCK_SCHEDULER();
}
PR_PUBLIC_API(PRThread *) PR_AttachThread(char *name, int priority,
PRThreadStack *stack)
{
PRThread *thread;
LOCK_SCHEDULER();
thread = (PRThread *) calloc( 1, sizeof(PRThread) );
if( thread ) {
/* Perform the PR_Create() initializations */
PR_INIT_CLIST(&thread->monitors);
PR_INIT_CLIST(&thread->runqLinks);
thread->name = strdup(name);
if( ! thread->name ) {
free(thread);
thread = 0;
goto done;
}
/* Create the Event object used by condition variables */
thread->condEvent = CreateEvent(NULL, FALSE, FALSE, NULL );
if (!thread->condEvent) {
free(thread);
thread = 0;
goto done;
}
thread->state = _PR_RUNNING;
thread->id = GetCurrentThreadId();
/*
** Warning:
** --------
** NSPR requires a real handle to every thread. GetCurrentThread()
** returns a pseudo-handle which is not suitable for some thread
** operations (ie. suspending). Therefore, get a real handle from
** the pseudo handle via DuplicateHandle(...)
*/
DuplicateHandle( GetCurrentProcess(), /* Process of source handle */
GetCurrentThread(), /* Pseudo Handle to duplicate */
GetCurrentProcess(), /* Process of target handle */
&(thread->handle), /* resulting handle */
0L, /* access flags */
FALSE, /* Inheritable */
DUPLICATE_SAME_ACCESS ); /* Options */
/* Perform all of the HopToad() initializations */
_pr_current_thread = thread;
/* Set up the thread stack information */
_MD_InitializeStack(stack);
thread->stack = stack;
/*
** 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.
*/
thread->priority = 100;
PR_SetThreadPriority( thread, priority );
PR_LOG(THREAD, out,
("%s(0X%x)[Start]: on to runq at priority %d",
thread->name, thread, thread->priority));
/* Update count of active threads */
if (thread->flags & _PR_SYSTEM) {
_pr_system_active++;
} else {
_pr_user_active++;
}
}
done:
UNLOCK_SCHEDULER();
return thread;
}
PR_PUBLIC_API(void) PR_DetachThread(PRThread *thread)
{
HANDLE hdl;
LOCK_SCHEDULER();
hdl = thread->handle;
PR_LOG(THREAD, out,
("%s(0X%x)[DetachThread]", thread->name, thread));
PR_REMOVE_LINK(&thread->runqLinks); /* take off runQ */
if (thread->flags & _PR_SYSTEM) {
_pr_system_active--;
PR_ASSERT(_pr_system_active >= 0);
} else {
_pr_user_active--;
PR_ASSERT(_pr_user_active >= 0);
}
/* Free the storage for the thread name */
if( thread->name ) {
free(thread->name);
thread->name = 0;
}
/* Free the storage for the thread structure */
free( thread );
/* Release the handle that was duplicated in PR_AttachThread(...) */
CloseHandle( hdl );
if (_pr_user_active == 0) {
UNLOCK_SCHEDULER();
exit(0);
}
UNLOCK_SCHEDULER();
}
PR_PUBLIC_API(int) PR_SetThreadPrivate(PRThread *t, int32 id, void GCPTR *priv)
{
int rv;
PRPerThreadData **pptd = &t->ptd;
PRPerThreadData *ptd;
rv = 0;
LOCK_SCHEDULER();
while ((ptd = *pptd) != 0) {
if (ptd->id == id) {
ptd->priv = priv;
rv = 1;
goto done;
}
pptd = &ptd->next;
}
ptd = calloc(1, sizeof(PRPerThreadData));/* XXX switch to GC heap */
if (ptd) {
ptd->id = id;
ptd->priv = priv;
ptd->next = 0;
*pptd = ptd;
rv = 1;
goto done;
}
done:
UNLOCK_SCHEDULER();
return rv;
}
PR_PUBLIC_API(void) GCPTR *PR_GetThreadPrivate(PRThread *t, int32 id)
{
void GCPTR *private;
PRPerThreadData *ptd;
LOCK_SCHEDULER();
ptd = t->ptd;
private = 0;
while (ptd) {
if (ptd->id == id) {
private = ptd->priv;
break;
}
ptd = ptd->next;
}
UNLOCK_SCHEDULER();
return private;
}
PR_PUBLIC_API(PRThread *) PR_CurrentThread()
{
return _pr_current_thread;
}
PR_PUBLIC_API(void) PR_SetError(int oserror)
{
PRThread *thread = _pr_current_thread;
thread->errcode = oserror;
}
PR_PUBLIC_API(int) PR_GetError(void)
{
PRThread *thread = _pr_current_thread;
return thread->errcode;
}
PR_PUBLIC_API(char*) PR_GetErrorString(void)
{
PRThread *thread = _pr_current_thread;
char* str;
LOCK_SCHEDULER();
str = thread->errstr;
thread->errstr = NULL;
UNLOCK_SCHEDULER();
return (str ? str : "");
}
PR_PUBLIC_API(char *) PR_GetThreadName(PRThread *t)
{
return t->name;
}
PR_PUBLIC_API(int32) PR_GetCurrentThreadID(void)
{
PRThread *thread = _pr_current_thread;
return thread->id;
}
PR_PUBLIC_API(int) PR_PendingException(PRThread *thread)
{
return thread->pendingException;
}
PR_PUBLIC_API(void) PR_SetPendingException(PRThread *thread)
{
LOCK_SCHEDULER();
PR_LOG(THREAD, out,
("%s(0X%x)[SetPendingException]", thread->name, thread));
if( thread->pendingException++ == 0 ) {
switch (thread->state) {
case _PR_COND_WAIT: {
/* Kick the thread out of COND_WAIT state */
PR_REMOVE_AND_INIT_LINK(&thread->waitLinks); /* remove from condQ */
PulseEvent( thread->condEvent );
PR_LOG(THREAD, out,
("%s(0X%x)[SetPendingException] kick out of CondWait",
thread->name, thread));
break;
}
case _PR_SLEEPING:
/* Kick the thread out of Sleep state */
PulseEvent( thread->condEvent );
PR_LOG(THREAD, out,
("%s(0X%x)[SetPendingException] kick out of Sleep",
thread->name, thread));
break;
}
}
UNLOCK_SCHEDULER();
}
PR_PUBLIC_API(void) PR_ClearPendingException(PRThread *thread)
{
LOCK_SCHEDULER();
PR_LOG(THREAD, out,
("%s(0X%x)[ClearPendingException]", thread->name, thread));
thread->pendingException--;
UNLOCK_SCHEDULER();
}
#endif /* HW_THREADS */