mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
10 KiB
479 lines
10 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Jim Seidman [email protected]
|
|
*/
|
|
|
|
#include "all.h"
|
|
|
|
/* Internally used structures ******************************/
|
|
struct FuncInfo {
|
|
struct FuncInfo *pfiParent; /* Index of function we're blocking, or 0 if this is topmost */
|
|
AsyncFunc afTheFunc; /* The address of this function */
|
|
int nState; /* State for next time we call this function */
|
|
void *pInfo; /* Data for this function */
|
|
};
|
|
|
|
struct ThreadInfo {
|
|
int nThreadID;
|
|
struct Mwin *mw; /* The window associated with this thread */
|
|
struct FuncInfo *pfiExecuting; /* Currently executing function */
|
|
struct ThreadInfo *ptiNext; /* Next thread in linked list, or NULL */
|
|
BOOL bBlocked; /* Is this thread explicitly blocked? */
|
|
BOOL bBeingNuked; /* Is this thread being nuked? */
|
|
};
|
|
|
|
/* Data arrays used by us *********************************/
|
|
static struct ThreadInfo *ptiThreads;
|
|
static struct FuncInfo *pfiFuncs;
|
|
static int nNextThreadID; /* ID for next thread created */
|
|
static struct ThreadInfo *ptiCurrent; /* Currently executing thread */
|
|
static struct ThreadInfo *ptiNext; /* Next thread to execute */
|
|
static BOOL bTerminateAll; /* Set when Async_TerminateAllThreads() called */
|
|
static BOOL bTerminateCurrent; /* Set when a thread terminates itself. */
|
|
|
|
/* Function definitions ***********************************/
|
|
|
|
static void Async_NukeThread(struct ThreadInfo *ptiVictim)
|
|
{
|
|
struct FuncInfo *pfiFunc;
|
|
struct ThreadInfo *ptiSave;
|
|
|
|
/* Make the thread getting nuked the current thread so that functions
|
|
can properly associate themselves with the right tw. */
|
|
ptiSave = ptiCurrent;
|
|
ptiCurrent = ptiVictim;
|
|
ptiVictim->bBeingNuked = TRUE;
|
|
while (ptiVictim->pfiExecuting)
|
|
{
|
|
pfiFunc = ptiVictim->pfiExecuting;
|
|
if (pfiFunc->nState == STATE_INIT)
|
|
{
|
|
/* We guarantee that a function will get called with STATE_INIT before
|
|
anything else happens to it. */
|
|
pfiFunc->nState = (*pfiFunc->afTheFunc)(ptiVictim->mw, pfiFunc->nState, &pfiFunc->pInfo);
|
|
}
|
|
|
|
/* This function may have started yet another function that we should
|
|
deal with first. */
|
|
if (pfiFunc != ptiVictim->pfiExecuting)
|
|
continue;
|
|
|
|
if (pfiFunc->nState != STATE_DONE)
|
|
{
|
|
(*pfiFunc->afTheFunc)(ptiVictim->mw, STATE_ABORT, &pfiFunc->pInfo);
|
|
}
|
|
/* Get rid of this function's information, and unblock the parent. */
|
|
if (pfiFunc->pInfo)
|
|
GTR_FREE(pfiFunc->pInfo);
|
|
ptiVictim->pfiExecuting = pfiFunc->pfiParent;
|
|
GTR_FREE(pfiFunc);
|
|
}
|
|
|
|
/* Remove this entry from the linked list */
|
|
if (ptiThreads == ptiVictim)
|
|
{
|
|
ptiThreads = ptiThreads->ptiNext;
|
|
}
|
|
else
|
|
{
|
|
struct ThreadInfo *ptiPrev;
|
|
|
|
for (ptiPrev = ptiThreads; ptiPrev && ptiPrev->ptiNext != ptiVictim; ptiPrev = ptiPrev->ptiNext)
|
|
;
|
|
XX_Assert((ptiPrev), ("Async_NukeThread: Couldn't find previous thread."));
|
|
ptiPrev->ptiNext = ptiVictim->ptiNext;
|
|
}
|
|
|
|
GTR_FREE(ptiVictim);
|
|
ptiCurrent = ptiSave;
|
|
}
|
|
|
|
void Async_Init(void)
|
|
{
|
|
/* Initialize everything */
|
|
ptiThreads = NULL;
|
|
pfiFuncs = NULL;
|
|
nNextThreadID = 1;
|
|
ptiCurrent = NULL;
|
|
ptiNext = NULL;
|
|
bTerminateAll = FALSE;
|
|
bTerminateCurrent = FALSE;
|
|
}
|
|
|
|
BOOL Async_KeepGoing(void)
|
|
{
|
|
struct FuncInfo *pfiFunc;
|
|
|
|
XX_Assert((ptiCurrent == NULL), ("Async_KeepGoing was called reentrantly!"));
|
|
|
|
if (!ptiNext)
|
|
ptiNext = ptiThreads;
|
|
|
|
if (!ptiNext)
|
|
{
|
|
/* There are no threads. */
|
|
return FALSE;
|
|
}
|
|
|
|
/* If this thread is blocked, find the first unblocked one. */
|
|
for (ptiCurrent = ptiNext; ptiCurrent && ptiCurrent->bBlocked; ptiCurrent = ptiCurrent->ptiNext)
|
|
;
|
|
if (!ptiCurrent)
|
|
{
|
|
for (ptiCurrent = ptiThreads;
|
|
ptiCurrent && ptiCurrent->bBlocked && ptiCurrent != ptiNext;
|
|
ptiCurrent = ptiCurrent->ptiNext)
|
|
;
|
|
/* ptiNext was blocked, so if we got back there, everything's blocked */
|
|
if (ptiCurrent == ptiNext)
|
|
ptiCurrent = NULL;
|
|
}
|
|
|
|
if (!ptiCurrent)
|
|
{
|
|
/* All the threads are blocked */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Do the function call */
|
|
pfiFunc = ptiCurrent->pfiExecuting;
|
|
pfiFunc->nState = (*pfiFunc->afTheFunc)(ptiCurrent->mw, pfiFunc->nState, &pfiFunc->pInfo);
|
|
|
|
/* See if the function completed */
|
|
if (pfiFunc->nState == STATE_DONE)
|
|
{
|
|
/* If the function did an Async_DoCall() before returning STATE_DONE,
|
|
block the parent. */
|
|
if (ptiCurrent->pfiExecuting != pfiFunc)
|
|
{
|
|
ptiCurrent->pfiExecuting->pfiParent = pfiFunc->pfiParent;
|
|
}
|
|
else
|
|
{
|
|
ptiCurrent->pfiExecuting = pfiFunc->pfiParent;
|
|
}
|
|
|
|
/* Get rid of this function's information, and unblock the parent. */
|
|
if (pfiFunc->pInfo)
|
|
GTR_FREE(pfiFunc->pInfo);
|
|
GTR_FREE(pfiFunc);
|
|
}
|
|
|
|
/* Figure out the next thread now, in case we free the current one. */
|
|
ptiNext = ptiCurrent->ptiNext;
|
|
|
|
/* If that was the root function for that thread, or if the thread tried
|
|
to terminate itself, release the thread from memory. */
|
|
if (ptiCurrent->pfiExecuting == NULL || bTerminateCurrent)
|
|
{
|
|
/* Actually nuke the thread now. */
|
|
Async_NukeThread(ptiCurrent);
|
|
bTerminateCurrent = FALSE;
|
|
}
|
|
|
|
/* If the program did an Async_TerminateAllThreads() call while within a
|
|
thread, we deal with it now. */
|
|
if (bTerminateAll)
|
|
{
|
|
/* Clean up all the threads. */
|
|
while (ptiThreads)
|
|
{
|
|
Async_NukeThread(ptiThreads);
|
|
}
|
|
}
|
|
|
|
ptiCurrent = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
void Async_DoCall(AsyncFunc afTarget, void *pParams)
|
|
{
|
|
struct FuncInfo *pfiNew;
|
|
|
|
pfiNew = GTR_MALLOC(sizeof(struct FuncInfo));
|
|
pfiNew->pfiParent = ptiCurrent->pfiExecuting;
|
|
pfiNew->afTheFunc = afTarget;
|
|
pfiNew->nState = STATE_INIT;
|
|
pfiNew->pInfo = pParams;
|
|
|
|
ptiCurrent->pfiExecuting = pfiNew;
|
|
}
|
|
|
|
ThreadID Async_StartThread(AsyncFunc afTarget, void *pParams, struct Mwin *mw)
|
|
{
|
|
struct ThreadInfo *ptiNew;
|
|
struct ThreadInfo *ptiPrior;
|
|
struct FuncInfo *pfiNew;
|
|
|
|
pfiNew = GTR_MALLOC(sizeof(struct FuncInfo));
|
|
pfiNew->pfiParent = NULL;
|
|
pfiNew->afTheFunc = afTarget;
|
|
pfiNew->nState = STATE_INIT;
|
|
pfiNew->pInfo = pParams;
|
|
|
|
ptiNew = GTR_MALLOC(sizeof(struct ThreadInfo));
|
|
ptiNew->nThreadID = nNextThreadID++;
|
|
ptiNew->mw = mw;
|
|
ptiNew->pfiExecuting = pfiNew;
|
|
ptiNew->bBlocked = FALSE;
|
|
ptiNew->bBeingNuked = FALSE;
|
|
|
|
// Run threads in the order spawned
|
|
|
|
ptiPrior = ptiThreads;
|
|
if (ptiPrior == NULL)
|
|
{
|
|
ptiNew->ptiNext = ptiThreads;
|
|
ptiThreads = ptiNew;
|
|
}
|
|
else
|
|
{
|
|
while (ptiPrior->ptiNext)
|
|
{
|
|
ptiPrior = ptiPrior->ptiNext;
|
|
}
|
|
ptiNew->ptiNext = NULL;
|
|
ptiPrior->ptiNext = ptiNew;
|
|
}
|
|
|
|
return ptiNew;
|
|
}
|
|
|
|
ThreadID Async_GetCurrentThread(void)
|
|
{
|
|
return ptiCurrent;
|
|
}
|
|
|
|
BOOL Async_OnLightweightThread(void)
|
|
{
|
|
return(Async_GetCurrentThread() != NULL);
|
|
}
|
|
|
|
struct Mwin *Async_GetWindowFromThread(ThreadID ID)
|
|
{
|
|
struct Mwin *mwResult;
|
|
|
|
if (ID)
|
|
mwResult = ID->mw;
|
|
else
|
|
{
|
|
XX_DMsg(DBG_ASYNC,("Async_GetWindowFromThread: returning NULL\n"));
|
|
mwResult = NULL;
|
|
}
|
|
|
|
return mwResult;
|
|
}
|
|
|
|
ThreadID Async_GetThreadForWindow(struct Mwin *mw)
|
|
{
|
|
struct ThreadInfo *pti;
|
|
#ifdef FEATURE_IMG_THREADS
|
|
struct Mwin *pw;
|
|
#endif
|
|
|
|
XX_Assert((mw), ("Async_GetThreadForWindow: non-NULL mw"));
|
|
pti = ptiThreads;
|
|
while (pti)
|
|
{
|
|
#ifdef FEATURE_IMG_THREADS
|
|
pw = pti->mw;
|
|
while (pw)
|
|
{
|
|
if (pw == mw)
|
|
{
|
|
return pti;
|
|
}
|
|
pw = pw->twParent;
|
|
}
|
|
#else
|
|
if (pti->mw == mw) return pti;
|
|
#endif
|
|
pti = pti->ptiNext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void Async_TerminateByWindow(struct Mwin *mw)
|
|
{
|
|
struct ThreadInfo *pti;
|
|
struct ThreadInfo *ptiIgnore = NULL;
|
|
#ifdef FEATURE_IMG_THREADS
|
|
struct Mwin *pw;
|
|
#endif
|
|
|
|
XX_Assert((mw), ("TerminateByWindow: non-NULL mw"));
|
|
pti = ptiThreads;
|
|
while (pti)
|
|
{
|
|
if (pti != ptiIgnore && !pti->bBeingNuked)
|
|
{
|
|
#ifdef FEATURE_IMG_THREADS
|
|
pw = pti->mw;
|
|
while (pw)
|
|
{
|
|
if (pw == mw)
|
|
{
|
|
// ptiCurrent isn't really terminated, a flag is just set
|
|
if (pti == ptiCurrent) ptiIgnore = pti;
|
|
Async_TerminateThread(pti);
|
|
if (ptiIgnore != pti) pti = NULL;
|
|
break;
|
|
}
|
|
pw = pw->twParent;
|
|
}
|
|
#else
|
|
if (pti->mw == mw)
|
|
{
|
|
Async_TerminateThread(pti);
|
|
pti = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
if (pti != NULL) pti = pti->ptiNext;
|
|
else pti = ptiThreads;
|
|
}
|
|
}
|
|
|
|
|
|
void Async_BlockByWindow(struct Mwin *mw)
|
|
{
|
|
struct ThreadInfo *pti;
|
|
|
|
for (pti = ptiThreads; pti; pti = pti->ptiNext)
|
|
{
|
|
if (pti->mw == mw)
|
|
{
|
|
Async_BlockThread(pti);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Async_UnblockByWindow(struct Mwin *mw)
|
|
{
|
|
struct ThreadInfo *pti;
|
|
|
|
for (pti = ptiThreads; pti; pti = pti->ptiNext)
|
|
{
|
|
if (pti->mw == mw)
|
|
{
|
|
Async_UnblockThread(pti);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef FEATURE_IMG_THREADS
|
|
/* Unblock all threads such that FilterProc returns true. FilterProc
|
|
is passed ThreadID and context, returns boolean.
|
|
*/
|
|
void Async_UnblockConditionally(AsyncFilter FilterProc,void *context)
|
|
{
|
|
struct ThreadInfo *pti;
|
|
|
|
for (pti = ptiThreads; pti; pti = pti->ptiNext)
|
|
{
|
|
if ((*FilterProc)(pti,context))
|
|
{
|
|
Async_UnblockThread(pti);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void Async_TerminateThread(ThreadID ID)
|
|
{
|
|
XX_DMsg(DBG_ASYNC, ("Async_TerminateThread: Terminating thread %d\n", ID));
|
|
|
|
/* We need to special-case the situation where a thread terminates itself,
|
|
since if we just removed that thread it would screw up the main loop when we
|
|
got back there. */
|
|
if (ID == ptiCurrent)
|
|
{
|
|
if (!ptiCurrent->bBeingNuked)
|
|
bTerminateCurrent = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (ptiNext == ID)
|
|
ptiNext = ID->ptiNext;
|
|
Async_NukeThread(ID);
|
|
}
|
|
}
|
|
|
|
void Async_TerminateAllThreads(void)
|
|
{
|
|
/* We can only really do the termination now if the program isn't inside
|
|
a thread. Otherwise deleting that thread could leave the program in
|
|
an unstable state. So, if we are in a thread, we just set a flag and
|
|
take care of it when the thread ends. */
|
|
bTerminateAll = TRUE;
|
|
if (!ptiCurrent)
|
|
{
|
|
/* Clean up all the threads. */
|
|
while (ptiThreads)
|
|
{
|
|
Async_NukeThread(ptiThreads);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Async_BlockThread(ThreadID ID)
|
|
{
|
|
if ( ID )
|
|
ID->bBlocked = TRUE;
|
|
}
|
|
|
|
void Async_UnblockThread(ThreadID ID)
|
|
{
|
|
if ( ID )
|
|
ID->bBlocked = FALSE;
|
|
}
|
|
|
|
/* Returns TRUE if any threads exist */
|
|
|
|
BOOL Async_DoThreadsExist(void)
|
|
{
|
|
return (ptiThreads != NULL);
|
|
}
|
|
|
|
/* mw is about to be freed, blast the mw field of all async procs so they know */
|
|
|
|
void Async_WindowWillBeFreed(struct Mwin *mw)
|
|
{
|
|
struct ThreadInfo *pti;
|
|
|
|
for (pti = ptiThreads; pti; pti = pti->ptiNext)
|
|
{
|
|
if (pti->mw == mw) pti->mw = NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef UNIX
|
|
#ifdef __CODECENTER__
|
|
|
|
|
|
void Async_TraceStack ()
|
|
{
|
|
struct FuncInfo *p;
|
|
char buf[1024];
|
|
|
|
if (!ptiCurrent)
|
|
return;
|
|
|
|
p = ptiCurrent->pfiExecuting; /* Currently executing function */
|
|
|
|
while (p) {
|
|
sprintf (buf, "(int (*)()) 0x%lx", p->afTheFunc);
|
|
centerline_print (buf);
|
|
p = p->pfiParent;
|
|
}
|
|
}
|
|
|
|
#endif /* __CODECENTER__ */
|
|
#endif /* UNIX */
|