TONY: Added support for Windows-style threading events to scheduler

This commit is contained in:
Paul Gilbert 2012-05-08 09:32:21 +10:00
parent 8527302057
commit a254f10025
8 changed files with 137 additions and 47 deletions

View file

@ -1310,10 +1310,17 @@ DECLARE_CUSTOM_FUNCTION(SetTonyPosition)(CORO_PARAM, uint32 nX, uint32 nY, uint3
}
DECLARE_CUSTOM_FUNCTION(MoveTonyAndWait)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
Tony->Move(RMPoint(nX, nY));
if (!bSkipIdle)
Tony->WaitForEndMovement();
CORO_INVOKE_0(Tony->WaitForEndMovement);
CORO_END_CODE;
}
DECLARE_CUSTOM_FUNCTION(MoveTony)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) {

View file

@ -835,12 +835,12 @@ RMItem::RMItem() {
m_bPal = 0;
m_nCurSprite = 0;
m_hEndPattern = _vm->_scheduler.createEvent(false, false);
m_hEndPattern = g_scheduler->createEvent(false, false);
}
RMItem::~RMItem() {
Unload();
_vm->_scheduler.closeEvent(m_hEndPattern);
g_scheduler->closeEvent(m_hEndPattern);
}
//FIXME: Pass uint32 directly for hCustomSkip
@ -853,11 +853,11 @@ void RMItem::WaitForEndPattern(CORO_PARAM, HANDLE hCustomSkip) {
if (m_nCurPattern != 0) {
if (hCustomSkip == INVALID_HANDLE_VALUE)
CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, m_hEndPattern, INFINITE);
CORO_INVOKE_2(g_scheduler->waitForSingleObject, m_hEndPattern, INFINITE);
else {
_ctx->h[0] = (uint32)hCustomSkip;
_ctx->h[1] = m_hEndPattern;
CORO_INVOKE_4(_vm->_scheduler.waitForMultipleObjects, 2, &_ctx->h[0], false, INFINITE);
CORO_INVOKE_4(g_scheduler->waitForMultipleObjects, 2, &_ctx->h[0], false, INFINITE);
}
}
@ -1082,7 +1082,7 @@ void RMCharacter::GoTo(RMPoint destcoord, bool bReversed) {
if (m_pos == destcoord) {
if (minpath == 0) {
Stop();
PulseEvent(hEndOfPath);
g_scheduler->pulseEvent(hEndOfPath);
return;
}
}
@ -1458,7 +1458,7 @@ void RMCharacter::DoFrame(RMGfxTargetBuffer* bigBuf, int loc) {
if (!bEndOfPath)
Stop();
bEndOfPath = true;
PulseEvent(hEndOfPath);
g_scheduler->pulseEvent(hEndOfPath);
}
walkcount++;
@ -1496,7 +1496,7 @@ void RMCharacter::DoFrame(RMGfxTargetBuffer* bigBuf, int loc) {
if (!bEndOfPath)
Stop();
bEndOfPath = true;
PulseEvent(hEndOfPath);
g_scheduler->pulseEvent(hEndOfPath);
}
} else {
// Se siamo già entrati nell'ultimo box, dobbiamo solo muoverci in linea retta verso il
@ -1636,6 +1636,18 @@ void RMCharacter::SetPosition(RMPoint pt, int newloc) {
bRemoveFromOT = true;
}
void RMCharacter::WaitForEndMovement(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (bMoving)
CORO_INVOKE_2(g_scheduler->waitForSingleObject, hEndOfPath, INFINITE);
CORO_END_CODE;
}
bool RMCharacter::RemoveThis(void) {
if (bRemoveFromOT)
return true;
@ -1645,7 +1657,7 @@ bool RMCharacter::RemoveThis(void) {
RMCharacter::RMCharacter() {
csMove = g_system->createMutex();
hEndOfPath = CreateEvent(NULL, false, false, NULL);
hEndOfPath = g_scheduler->createEvent(false, false);
minpath = 0;
curSpeed = 3;
bRemoveFromOT = false;
@ -1666,7 +1678,7 @@ RMCharacter::RMCharacter() {
RMCharacter::~RMCharacter() {
g_system->deleteMutex(csMove);
CloseHandle(hEndOfPath);
g_scheduler->closeEvent(hEndOfPath);
}
void RMCharacter::LinkToBoxes(RMGameBoxes *boxes) {

View file

@ -423,7 +423,7 @@ private:
STATUS status;
int curSpeed;
bool bEndOfPath;
HANDLE hEndOfPath;
uint32 hEndOfPath;
OSystem::MutexRef csMove;
int curLocation;
bool bRemoveFromOT;
@ -482,7 +482,7 @@ public:
void SetPosition(RMPoint pt, int newloc=-1);
// Aspetta la fine del movimento
void WaitForEndMovement(void) { if (bMoving) WaitForSingleObject(hEndOfPath, INFINITE); }
void WaitForEndMovement(CORO_PARAM);
void SetFixedScroll(RMPoint fix) { m_fixedScroll = fix; }
void SetSpeed(int speed) { curSpeed=speed; }

View file

@ -961,7 +961,7 @@ void ActionThread(CORO_PARAM, const void *param) {
if (item->Command[_ctx->k].type == 1) {
// Custom function
debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Call=%s params=%d,%d,%d,%d",
_vm->_scheduler.getCurrentPID(), lplpFunctionStrings[item->Command[_ctx->k].nCf].c_str(),
g_scheduler->getCurrentPID(), lplpFunctionStrings[item->Command[_ctx->k].nCf].c_str(),
item->Command[_ctx->k].arg1, item->Command[_ctx->k].arg2,
item->Command[_ctx->k].arg3, item->Command[_ctx->k].arg4
);
@ -976,7 +976,7 @@ void ActionThread(CORO_PARAM, const void *param) {
} else if (item->Command[_ctx->k].type == 2) {
// Variable assign
debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Variable=%s",
_vm->_scheduler.getCurrentPID(), item->Command[_ctx->k].lpszVarName);
g_scheduler->getCurrentPID(), item->Command[_ctx->k].lpszVarName);
LockVar();
varSetValue(item->Command[_ctx->k].lpszVarName, EvaluateExpression(item->Command[_ctx->k].expr));
@ -991,7 +991,7 @@ void ActionThread(CORO_PARAM, const void *param) {
GlobalFree(item);
debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", _vm->_scheduler.getCurrentPID());
debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", g_scheduler->getCurrentPID());
CORO_KILL_SELF();
@ -1012,7 +1012,7 @@ void ShutUpActionThread(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, pid, INFINITE);
CORO_INVOKE_2(g_scheduler->waitForSingleObject, pid, INFINITE);
bExecutingAction = false;
@ -1190,7 +1190,7 @@ void LocationPollThread(CORO_PARAM, const void *param) {
for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++)
if (_ctx->MyThreads[_ctx->i].nItem != 0) {
CORO_INVOKE_3(_vm->_scheduler.waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 0, &_ctx->delayExpired);
CORO_INVOKE_3(g_scheduler->waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 0, &_ctx->delayExpired);
// if result ) == WAIT_OBJECT_0)
if (!_ctx->delayExpired)
@ -1261,7 +1261,7 @@ void LocationPollThread(CORO_PARAM, const void *param) {
_ctx->MyThreads[_ctx->i].nItem = _ctx->MyActions[_ctx->k].nItem;
// !!! Nuova gestione dei thread
if ((_ctx->MyThreads[_ctx->i].hThread = _vm->_scheduler.createProcess(ActionThread, &_ctx->newItem, sizeof(LPMPALITEM))) == 0) {
if ((_ctx->MyThreads[_ctx->i].hThread = g_scheduler->createProcess(ActionThread, &_ctx->newItem, sizeof(LPMPALITEM))) == 0) {
//if ((_ctx->MyThreads[_ctx->i].hThread=(void*)_beginthread(ActionThread, 10240,(void *)_ctx->newItem))==(void*)-1)
GlobalFree(_ctx->newItem);
GlobalFree(_ctx->MyThreads);
@ -1295,14 +1295,14 @@ void LocationPollThread(CORO_PARAM, const void *param) {
for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++)
if (_ctx->MyThreads[_ctx->i].nItem != 0) {
CORO_INVOKE_3(_vm->_scheduler.waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 5000, &_ctx->delayExpired);
CORO_INVOKE_3(g_scheduler->waitForSingleObject, _ctx->MyThreads[_ctx->i].hThread, 5000, &_ctx->delayExpired);
/*
//if (result != WAIT_OBJECT_0)
if (_ctx->delayExpired)
TerminateThread(_ctx->MyThreads[_ctx->i].hThread, 0);
*/
_vm->_scheduler.killMatchingProcess(_ctx->MyThreads[_ctx->i].hThread);
g_scheduler->killMatchingProcess(_ctx->MyThreads[_ctx->i].hThread);
}
// Set idle skip off
@ -1343,13 +1343,13 @@ void ShutUpDialogThread(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, pid, INFINITE);
CORO_INVOKE_2(g_scheduler->waitForSingleObject, pid, INFINITE);
bExecutingDialog = false;
nExecutingDialog = 0;
nExecutingChoice = 0;
_vm->_scheduler.setEvent(hAskChoice);
g_scheduler->setEvent(hAskChoice);
CORO_KILL_SELF();
@ -1501,9 +1501,9 @@ void DoChoice(CORO_PARAM, uint32 nChoice) {
/* Avvertiamo il gioco che c'e' una scelta da far fare all'utente,
e restiamo in attesa della risposta */
_vm->_scheduler.resetEvent(hDoneChoice);
_vm->_scheduler.setEvent(hAskChoice);
CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, hDoneChoice, INFINITE);
g_scheduler->resetEvent(hDoneChoice);
g_scheduler->setEvent(hAskChoice);
CORO_INVOKE_2(g_scheduler->waitForSingleObject, hDoneChoice, INFINITE);
/* Ora che la scelta e' stata effettuata, possiamo eseguire _ctx->i gruppi
associati con la scelta */
@ -1644,19 +1644,19 @@ static uint32 DoDialog(uint32 nDlgOrd, uint32 nGroup) {
// Enables the flag to indicate that there is' a running dialogue
bExecutingDialog = true;
_vm->_scheduler.resetEvent(hAskChoice);
_vm->_scheduler.resetEvent(hDoneChoice);
g_scheduler->resetEvent(hAskChoice);
g_scheduler->resetEvent(hDoneChoice);
// Create a thread that performs the dialogue group
// Create the process
if ((h = _vm->_scheduler.createProcess(GroupThread, &nGroup, sizeof(uint32))) == 0)
if ((h = g_scheduler->createProcess(GroupThread, &nGroup, sizeof(uint32))) == 0)
return 0;
// Create a thread that waits until the end of the dialog process, and will restore the global variables
if (_vm->_scheduler.createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == 0) {
if (g_scheduler->createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == 0) {
// Something went wrong, so kill the previously started dialog process
_vm->_scheduler.killMatchingProcess(h);
g_scheduler->killMatchingProcess(h);
return 0;
}
@ -1690,7 +1690,7 @@ bool DoSelection(uint32 i, uint32 dwData) {
return false;
nSelectedChoice = j;
_vm->_scheduler.setEvent(hDoneChoice);
g_scheduler->setEvent(hDoneChoice);
return true;
}
@ -1867,8 +1867,8 @@ bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName,
/* Crea l'evento che verra' utilizzato per avvertire il gioco che c'e'
da effettuare una scelta */
hAskChoice = _vm->_scheduler.createEvent(true, false);
hDoneChoice = _vm->_scheduler.createEvent(true, false);
hAskChoice = g_scheduler->createEvent(true, false);
hDoneChoice = g_scheduler->createEvent(true, false);
return true;
}
@ -2031,9 +2031,9 @@ void mpalQueryInner(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, va_list v) {
/*
* void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
*/
CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, hAskChoice, INFINITE);
CORO_INVOKE_2(g_scheduler->waitForSingleObject, hAskChoice, INFINITE);
_vm->_scheduler.resetEvent(hAskChoice);
g_scheduler->resetEvent(hAskChoice);
if (bExecutingDialog)
*dwRet = (uint32)nExecutingChoice;
@ -2217,7 +2217,7 @@ bool mpalStartIdlePoll(int nLoc) {
hEndPollingLocations[i] = CreateEvent(NULL, true, false, NULL);
// !!! Nuova gestione dei thread
if ((PollingThreads[i] = _vm->_scheduler.createProcess(LocationPollThread, &i, sizeof(uint32))) == 0)
if ((PollingThreads[i] = g_scheduler->createProcess(LocationPollThread, &i, sizeof(uint32))) == 0)
// if ((hEndPollingLocations[i]=(void*)_beginthread(LocationPollThread, 10240,(void *)i))==(void*)-1)
return false;
@ -2254,7 +2254,7 @@ void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result) {
if (nPollingLocations[_ctx->i] == (uint32)nLoc) {
SetEvent(hEndPollingLocations[_ctx->i]);
CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, PollingThreads[_ctx->i], INFINITE);
CORO_INVOKE_2(g_scheduler->waitForSingleObject, PollingThreads[_ctx->i], INFINITE);
CloseHandle(hEndPollingLocations[_ctx->i]);
nPollingLocations[_ctx->i] = 0;

View file

@ -51,6 +51,8 @@ Scheduler::Scheduler() {
active->pNext = NULL;
g_scheduler = this; // FIXME HACK
reset();
}
Scheduler::~Scheduler() {
@ -102,6 +104,7 @@ void Scheduler::reset() {
while (pProc != NULL) {
delete pProc->state;
pProc->state = 0;
pProc->waiting = false;
pProc = pProc->pNext;
}
@ -315,6 +318,9 @@ void Scheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool *
CORO_BEGIN_CODE(_ctx);
// Signal as waiting
pCurrent->waiting = true;
_ctx->endTime = (duration == INFINITE) ? INFINITE : g_system->getMillis() + duration;
if (expired)
// Presume it will expire
@ -350,6 +356,9 @@ void Scheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool *
CORO_SLEEP(1);
}
// Signal waiting is done
pCurrent->waiting = false;
CORO_END_CODE;
}
@ -378,6 +387,9 @@ void Scheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList,
CORO_BEGIN_CODE(_ctx);
// Signal as waiting
pCurrent->waiting = true;
_ctx->endTime = (duration == INFINITE) ? INFINITE : g_system->getMillis() + duration;
if (expired)
// Presume that delay will expire
@ -419,6 +431,9 @@ void Scheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList,
CORO_SLEEP(1);
}
// Signal waiting is done
pCurrent->waiting = false;
CORO_END_CODE;
}
@ -567,12 +582,12 @@ int Scheduler::getCurrentPID() const {
* @param pidMask mask to apply to process identifiers before comparison
* @return The number of processes killed is returned.
*/
int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
int Scheduler::killMatchingProcess(uint32 pidKill, int pidMask) {
int numKilled = 0;
PROCESS *pProc, *pPrev; // process list pointers
for (pProc = active->pNext, pPrev = active; pProc != NULL; pPrev = pProc, pProc = pProc->pNext) {
if ((pProc->pid & pidMask) == pidKill) {
if ((pProc->pid & (uint32)pidMask) == pidKill) {
// found a matching process
// dont kill the current process
@ -697,4 +712,56 @@ void Scheduler::resetEvent(uint32 pidEvent) {
evt->signalled = false;
}
/**
* Temporarily sets a given event to true, and then runs all waiting processes, allowing any
* processes waiting on the event to be fired. It then immediately resets the event again.
* @param pidEvent Event PID
*
* @remarks Should not be run inside of another process
*/
void Scheduler::pulseEvent(uint32 pidEvent) {
EVENT *evt = getEvent(pidEvent);
if (!evt)
return;
// Set the event as true
evt->signalled = true;
// start dispatching active process list for any processes that are currently waiting
PROCESS *pOriginal = pCurrent;
PROCESS *pNext;
PROCESS *pProc = active->pNext;
while (pProc != NULL) {
pNext = pProc->pNext;
// Only call processes that are currently waiting (either in waitForSingleObject or
// waitForMultipleObjects). If one is found, execute it immediately
if (pProc->waiting) {
// Dispatch the process
pCurrent = pProc;
pProc->coroAddr(pProc->state, pProc->param);
if (!pProc->state || pProc->state->_sleep <= 0) {
// Coroutine finished
pCurrent = pCurrent->pPrevious;
killProcess(pProc);
} else {
pProc->sleepTime = pProc->state->_sleep;
}
// pCurrent may have been changed
pNext = pCurrent->pNext;
pCurrent = NULL;
}
pProc = pNext;
}
// Restore the original current process (if one was active)
pCurrent = pOriginal;
// Reset the event back to non-signalled
evt->signalled = false;
}
} // End of namespace Tony

View file

@ -50,6 +50,7 @@ struct PROCESS {
int sleepTime; ///< number of scheduler cycles to sleep
uint32 pid; ///< process ID
bool waiting; ///< process is currently in a waiting state
char param[PARAM_SIZE]; ///< process specific info
};
typedef PROCESS *PPROCESS;
@ -134,7 +135,7 @@ public:
PROCESS *getCurrentProcess();
int getCurrentPID() const;
int killMatchingProcess(int pidKill, int pidMask = -1);
int killMatchingProcess(uint32 pidKill, int pidMask = -1);
void setResourceCallback(VFPTRPP pFunc);
@ -143,6 +144,7 @@ public:
void closeEvent(uint32 pidEvent);
void setEvent(uint32 pidEvent);
void resetEvent(uint32 pidEvent);
void pulseEvent(uint32 pidEvent);
};
extern Scheduler *g_scheduler; // FIXME: Temporary global var, to be used until everything has been OOifyied

View file

@ -36,7 +36,7 @@ namespace Tony {
TonyEngine *_vm;
TonyEngine::TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc) : Engine(syst),
_gameDescription(gameDesc), _randomSource("tony") {
_gameDescription(gameDesc), _randomSource("tony"), _scheduler() {
_vm = this;
DebugMan.addDebugChannel(kTonyDebugAnimations, "animations", "Animations debugging");

View file

@ -67,7 +67,7 @@ void RMTony::WaitEndOfAction(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
CORO_INVOKE_2(_vm->_scheduler.waitForSingleObject, pid, INFINITE);
CORO_INVOKE_2(g_scheduler->waitForSingleObject, pid, INFINITE);
m_bAction = false;
@ -240,26 +240,26 @@ void RMTony::ExecuteAction(int nAction, int nActionItem, int nParm) {
if (hThread != INVALID_HANDLE_VALUE) {
m_bAction = true;
pid = (uint32)hThread;
_vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32));
g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32));
hActionThread = hThread;
} else if (nAction != TA_GOTO) {
if (nAction == TA_TALK) {
hThread = mpalQueryDoAction(6, 1, 0);
m_bAction = true;
pid = (uint32)hThread;
_vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32));
g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32));
hActionThread = hThread;
} else if (nAction == TA_PALESATI) {
hThread = mpalQueryDoAction(7, 1, 0);
m_bAction = true;
pid = (uint32)hThread;
_vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32));
g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32));
hActionThread=hThread;
} else {
hThread = mpalQueryDoAction(5, 1, 0);
m_bAction = true;
pid = (uint32)hThread;
_vm->_scheduler.createProcess(WaitEndOfAction, &pid, sizeof(uint32));
g_scheduler->createProcess(WaitEndOfAction, &pid, sizeof(uint32));
hActionThread = hThread;
}
}
@ -1075,6 +1075,8 @@ bool RMTony::StartTalkCalculate(TALKTYPE nTalkType, int &headStartPat, int &body
}
break;
}
return true;
}
void RMTony::StartTalk(CORO_PARAM, TALKTYPE nTalkType) {