aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorMatthew Hoops2012-06-16 01:37:40 -0400
committerMatthew Hoops2012-06-16 01:43:32 -0400
commit625f6cc71657e95e0361edefa333a38910c1aca5 (patch)
treeae5ef8582ede4d62e56253467ea144db2341b353 /common
parentf02b696573fe4281e4890d71b74671804a5ebf41 (diff)
parent5230a0d61795e2855625a43d60dc3bc2ed83fc3d (diff)
downloadscummvm-rg350-625f6cc71657e95e0361edefa333a38910c1aca5.tar.gz
scummvm-rg350-625f6cc71657e95e0361edefa333a38910c1aca5.tar.bz2
scummvm-rg350-625f6cc71657e95e0361edefa333a38910c1aca5.zip
Merge remote branch 'upstream/master' into pegasus
Diffstat (limited to 'common')
-rw-r--r--common/coroutines.cpp183
-rw-r--r--common/coroutines.h308
-rw-r--r--common/system.h42
-rw-r--r--common/taskbar.h2
-rw-r--r--common/updates.h2
5 files changed, 278 insertions, 259 deletions
diff --git a/common/coroutines.cpp b/common/coroutines.cpp
index d511ab4b35..241d31e0d7 100644
--- a/common/coroutines.cpp
+++ b/common/coroutines.cpp
@@ -34,14 +34,17 @@ CoroContext nullContext = NULL;
DECLARE_SINGLETON(CoroutineScheduler);
-
#ifdef COROUTINE_DEBUG
namespace {
+/** Count of active coroutines */
static int s_coroCount = 0;
typedef Common::HashMap<Common::String, int> CoroHashMap;
static CoroHashMap *s_coroFuncs = 0;
+/**
+ * Change the current coroutine status
+ */
static void changeCoroStats(const char *func, int change) {
if (!s_coroFuncs)
s_coroFuncs = new CoroHashMap();
@@ -49,6 +52,9 @@ static void changeCoroStats(const char *func, int change) {
(*s_coroFuncs)[func] += change;
}
+/**
+ * Display the details of active coroutines
+ */
static void displayCoroStats() {
debug("%d active coros", s_coroCount);
@@ -56,13 +62,13 @@ static void displayCoroStats() {
if (!s_coroFuncs)
return;
for (CoroHashMap::const_iterator it = s_coroFuncs->begin();
- it != s_coroFuncs->end(); ++it) {
+ it != s_coroFuncs->end(); ++it) {
if (it->_value != 0)
debug(" %3d x %s", it->_value, it->_key.c_str());
}
}
-}
+} // End of anonymous namespace
#endif
CoroBaseContext::CoroBaseContext(const char *func)
@@ -79,7 +85,7 @@ CoroBaseContext::~CoroBaseContext() {
s_coroCount--;
changeCoroStats(_funcName, -1);
debug("Deleting coro in %s at %p (subctx %p)",
- _funcName, (void *)this, (void *)_subctx);
+ _funcName, (void *)this, (void *)_subctx);
displayCoroStats();
#endif
delete _subctx;
@@ -87,9 +93,6 @@ CoroBaseContext::~CoroBaseContext() {
//--------------------- Scheduler Class ------------------------
-/**
- * Constructor
- */
CoroutineScheduler::CoroutineScheduler() {
processList = NULL;
pFreeProcesses = NULL;
@@ -111,9 +114,6 @@ CoroutineScheduler::CoroutineScheduler() {
reset();
}
-/**
- * Destructor
- */
CoroutineScheduler::~CoroutineScheduler() {
// Kill all running processes (i.e. free memory allocated for their state).
PROCESS *pProc = active->pNext;
@@ -132,14 +132,10 @@ CoroutineScheduler::~CoroutineScheduler() {
// Clear the event list
Common::List<EVENT *>::iterator i;
for (i = _events.begin(); i != _events.end(); ++i)
- delete (*i);
+ delete *i;
}
-/**
- * Kills all processes and places them on the free list.
- */
void CoroutineScheduler::reset() {
-
#ifdef DEBUG
// clear number of process in use
numProcs = 0;
@@ -181,21 +177,14 @@ void CoroutineScheduler::reset() {
}
-#ifdef DEBUG
-/**
- * Shows the maximum number of process used at once.
- */
+#ifdef DEBUG
void CoroutineScheduler::printStats() {
debug("%i process of %i used", maxProcs, CORO_NUM_PROCESS);
}
#endif
#ifdef DEBUG
-/**
- * Checks both the active and free process list to insure all the links are valid,
- * and that no processes have been lost
- */
-void CoroutineScheduler::CheckStack() {
+void CoroutineScheduler::checkStack() {
Common::List<PROCESS *> pList;
// Check both the active and free process lists
@@ -229,9 +218,6 @@ void CoroutineScheduler::CheckStack() {
}
#endif
-/**
- * Give all active processes a chance to run
- */
void CoroutineScheduler::schedule() {
// start dispatching active process list
PROCESS *pNext;
@@ -261,9 +247,6 @@ void CoroutineScheduler::schedule() {
}
}
-/**
- * Reschedules all the processes to run again this query
- */
void CoroutineScheduler::rescheduleAll() {
assert(pCurrent);
@@ -279,10 +262,6 @@ void CoroutineScheduler::rescheduleAll() {
pCurrent->pPrevious = active;
}
-/**
- * If the specified process has already run on this tick, make it run
- * again on the current tick.
- */
void CoroutineScheduler::reschedule(PPROCESS pReSchedProc) {
// If not currently processing the schedule list, then no action is needed
if (!pCurrent)
@@ -320,11 +299,6 @@ void CoroutineScheduler::reschedule(PPROCESS pReSchedProc) {
pReSchedProc->pNext = NULL;
}
-/**
- * Moves the specified process to the end of the dispatch queue
- * allowing it to run again within the current game cycle.
- * @param pGiveProc Which process
- */
void CoroutineScheduler::giveWay(PPROCESS pReSchedProc) {
// If not currently processing the schedule list, then no action is needed
if (!pCurrent)
@@ -358,13 +332,6 @@ void CoroutineScheduler::giveWay(PPROCESS pReSchedProc) {
pReSchedProc->pNext = NULL;
}
-/**
- * Continously makes a given process wait for another process to finish or event to signal.
- *
- * @param pid Process/Event identifier
- * @param duration Duration in milliseconds
- * @param expired If specified, set to true if delay period expired
- */
void CoroutineScheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool *expired) {
if (!pCurrent)
error("Called CoroutineScheduler::waitForSingleObject from the main process");
@@ -385,8 +352,8 @@ void CoroutineScheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duratio
// Presume it will expire
*expired = true;
- // Outer loop for doing checks until expiry
- while (g_system->getMillis() < _ctx->endTime) {
+ // Outer loop for doing checks until expiry
+ while (g_system->getMillis() <= _ctx->endTime) {
// Check to see if a process or event with the given Id exists
_ctx->pProcess = getProcess(pid);
_ctx->pEvent = !_ctx->pProcess ? getEvent(pid) : NULL;
@@ -399,7 +366,7 @@ void CoroutineScheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duratio
break;
}
- // If a process was found, don't go into the if statement, and keep waiting.
+ // If a process was found, don't go into the if statement, and keep waiting.
// Likewise if it's an event that's not yet signalled
if ((_ctx->pEvent != NULL) && _ctx->pEvent->signalled) {
// Unless the event is flagged for manual reset, reset it now
@@ -421,19 +388,10 @@ void CoroutineScheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duratio
CORO_END_CODE;
}
-/**
- * Continously makes a given process wait for given prcesses to finished or events to be set
- *
- * @param nCount Number of Id's being passed
- * @param evtList List of pids to wait for
- * @param bWaitAll Specifies whether all or any of the processes/events
- * @param duration Duration in milliseconds
- * @param expired Set to true if delay period expired
- */
-void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, bool bWaitAll,
- uint32 duration, bool *expired) {
+void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, bool bWaitAll,
+ uint32 duration, bool *expired) {
if (!pCurrent)
- error("Called CoroutineScheduler::waitForMultipleEvents from the main process");
+ error("Called CoroutineScheduler::waitForMultipleObjects from the main process");
CORO_BEGIN_CONTEXT;
uint32 endTime;
@@ -455,8 +413,8 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *
// Presume that delay will expire
*expired = true;
- // Outer loop for doing checks until expiry
- while (g_system->getMillis() < _ctx->endTime) {
+ // Outer loop for doing checks until expiry
+ while (g_system->getMillis() <= _ctx->endTime) {
_ctx->signalled = bWaitAll;
for (_ctx->i = 0; _ctx->i < nCount; ++_ctx->i) {
@@ -497,15 +455,9 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *
CORO_END_CODE;
}
-/**
- * Make the active process sleep for the given duration in milliseconds
- * @param duration Duration in milliseconds
- * @remarks This duration won't be precise, since it relies on the frequency the
- * scheduler is called.
- */
void CoroutineScheduler::sleep(CORO_PARAM, uint32 duration) {
if (!pCurrent)
- error("Called CoroutineScheduler::waitForSingleObject from the main process");
+ error("Called CoroutineScheduler::sleep from the main process");
CORO_BEGIN_CONTEXT;
uint32 endTime;
@@ -517,7 +469,7 @@ void CoroutineScheduler::sleep(CORO_PARAM, uint32 duration) {
_ctx->endTime = g_system->getMillis() + duration;
- // Outer loop for doing checks until expiry
+ // Outer loop for doing checks until expiry
while (g_system->getMillis() < _ctx->endTime) {
// Sleep until the next cycle
CORO_SLEEP(1);
@@ -526,14 +478,6 @@ void CoroutineScheduler::sleep(CORO_PARAM, uint32 duration) {
CORO_END_CODE;
}
-/**
- * Creates a new process.
- *
- * @param pid process identifier
- * @param CORO_ADDR coroutine start address
- * @param pParam process specific info
- * @param sizeParam size of process specific info
- */
PROCESS *CoroutineScheduler::createProcess(uint32 pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam) {
PROCESS *pProc;
@@ -564,7 +508,7 @@ PROCESS *CoroutineScheduler::createProcess(uint32 pid, CORO_ADDR coroAddr, const
pCurrent->pNext = pProc;
pProc->pPrevious = pCurrent;
- } else { // no active processes, place process at head of list
+ } else { // no active processes, place process at head of list
pProc->pNext = active->pNext;
pProc->pPrevious = active;
@@ -598,35 +542,15 @@ PROCESS *CoroutineScheduler::createProcess(uint32 pid, CORO_ADDR coroAddr, const
return pProc;
}
-/**
- * Creates a new process with an auto-incrementing Process Id.
- *
- * @param CORO_ADDR coroutine start address
- * @param pParam process specific info
- * @param sizeParam size of process specific info
- */
uint32 CoroutineScheduler::createProcess(CORO_ADDR coroAddr, const void *pParam, int sizeParam) {
PROCESS *pProc = createProcess(++pidCounter, coroAddr, pParam, sizeParam);
return pProc->pid;
}
-/**
- * Creates a new process with an auto-incrementing Process Id, and a single pointer parameter.
- *
- * @param CORO_ADDR coroutine start address
- * @param pParam process specific info
- * @param sizeParam size of process specific info
- */
uint32 CoroutineScheduler::createProcess(CORO_ADDR coroAddr, const void *pParam) {
return createProcess(coroAddr, &pParam, sizeof(void *));
}
-
-/**
- * Kills the specified process.
- *
- * @param pKillProc which process to kill
- */
void CoroutineScheduler::killProcess(PROCESS *pKillProc) {
// make sure a valid process pointer
assert(pKillProc >= processList && pKillProc <= processList + CORO_NUM_PROCESS - 1);
@@ -662,20 +586,10 @@ void CoroutineScheduler::killProcess(PROCESS *pKillProc) {
pFreeProcesses = pKillProc;
}
-
-
-/**
- * Returns a pointer to the currently running process.
- */
PROCESS *CoroutineScheduler::getCurrentProcess() {
return pCurrent;
}
-/**
- * Returns the process identifier of the specified process.
- *
- * @param pProc which process
- */
int CoroutineScheduler::getCurrentPID() const {
PROCESS *pProc = pCurrent;
@@ -686,17 +600,9 @@ int CoroutineScheduler::getCurrentPID() const {
return pProc->pid;
}
-/**
- * Kills any process matching the specified PID. The current
- * process cannot be killed.
- *
- * @param pidKill process identifier of process to kill
- * @param pidMask mask to apply to process identifiers before comparison
- * @return The number of processes killed is returned.
- */
int CoroutineScheduler::killMatchingProcess(uint32 pidKill, int pidMask) {
int numKilled = 0;
- PROCESS *pProc, *pPrev; // process list pointers
+ PROCESS *pProc, *pPrev; // process list pointers
for (pProc = active->pNext, pPrev = active; pProc != NULL; pPrev = pProc, pProc = pProc->pNext) {
if ((pProc->pid & (uint32)pidMask) == pidKill) {
@@ -743,15 +649,6 @@ int CoroutineScheduler::killMatchingProcess(uint32 pidKill, int pidMask) {
return numKilled;
}
-/**
- * Set pointer to a function to be called by killProcess().
- *
- * May be called by a resource allocator, the function supplied is
- * called by killProcess() to allow the resource allocator to free
- * resources allocated to the dying process.
- *
- * @param pFunc Function to be called by killProcess()
- */
void CoroutineScheduler::setResourceCallback(VFPTRPP pFunc) {
pRCfunction = pFunc;
}
@@ -776,12 +673,6 @@ EVENT *CoroutineScheduler::getEvent(uint32 pid) {
}
-/**
- * Creates a new event object
- * @param bManualReset Events needs to be manually reset. Otherwise, events
- * will be automatically reset after a process waits on the event finishes
- * @param bInitialState Specifies whether the event is signalled or not initially
- */
uint32 CoroutineScheduler::createEvent(bool bManualReset, bool bInitialState) {
EVENT *evt = new EVENT();
evt->pid = ++pidCounter;
@@ -792,10 +683,6 @@ uint32 CoroutineScheduler::createEvent(bool bManualReset, bool bInitialState) {
return evt->pid;
}
-/**
- * Destroys the given event
- * @param pidEvent Event PID
- */
void CoroutineScheduler::closeEvent(uint32 pidEvent) {
EVENT *evt = getEvent(pidEvent);
if (evt) {
@@ -804,41 +691,26 @@ void CoroutineScheduler::closeEvent(uint32 pidEvent) {
}
}
-/**
- * Sets the event
- * @param pidEvent Event PID
- */
void CoroutineScheduler::setEvent(uint32 pidEvent) {
EVENT *evt = getEvent(pidEvent);
if (evt)
evt->signalled = true;
}
-/**
- * Resets the event
- * @param pidEvent Event PID
- */
void CoroutineScheduler::resetEvent(uint32 pidEvent) {
EVENT *evt = getEvent(pidEvent);
if (evt)
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 CoroutineScheduler::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;
@@ -880,5 +752,4 @@ void CoroutineScheduler::pulseEvent(uint32 pidEvent) {
evt->signalled = false;
}
-
} // end of namespace Common
diff --git a/common/coroutines.h b/common/coroutines.h
index 6df843887c..64eabbf8f4 100644
--- a/common/coroutines.h
+++ b/common/coroutines.h
@@ -23,7 +23,7 @@
#define COMMON_COROUTINES_H
#include "common/scummsys.h"
-#include "common/util.h" // for SCUMMVM_CURRENT_FUNCTION
+#include "common/util.h" // for SCUMMVM_CURRENT_FUNCTION
#include "common/list.h"
#include "common/singleton.h"
@@ -56,7 +56,14 @@ struct CoroBaseContext {
#ifdef COROUTINE_DEBUG
const char *_funcName;
#endif
+ /**
+ * Creates a coroutine context
+ */
CoroBaseContext(const char *func);
+
+ /**
+ * Destructor for coroutine context
+ */
virtual ~CoroBaseContext();
};
@@ -64,8 +71,9 @@ typedef CoroBaseContext *CoroContext;
/** This is a special constant that can be temporarily used as a parameter to call coroutine-ised
- * from methods from methods that haven't yet been converted to being a coroutine, so code at least
- * compiles correctly. Be aware, though, that if you use this, you will get runtime errors.
+ * methods from code that haven't yet been converted to being a coroutine, so code at least
+ * compiles correctly. Be aware, though, that an error will occur if a coroutine that was passed
+ * the nullContext tries to sleep or yield control.
*/
extern CoroContext nullContext;
@@ -125,42 +133,43 @@ public:
/**
* End the declaration of a coroutine context.
- * @param x name of the coroutine context
+ * @param x name of the coroutine context
* @see CORO_BEGIN_CONTEXT
*/
#define CORO_END_CONTEXT(x) } *x = (CoroContextTag *)coroParam
/**
* Begin the code section of a coroutine.
- * @param x name of the coroutine context
+ * @param x name of the coroutine context
* @see CORO_BEGIN_CODE
*/
#define CORO_BEGIN_CODE(x) \
- if (&coroParam == &Common::nullContext) assert(!Common::nullContext);\
- if (!x) {coroParam = x = new CoroContextTag();}\
- Common::CoroContextHolder tmpHolder(coroParam);\
- switch (coroParam->_line) { case 0:;
+ if (&coroParam == &Common::nullContext) assert(!Common::nullContext); \
+ if (!x) { coroParam = x = new CoroContextTag(); } \
+ Common::CoroContextHolder tmpHolder(coroParam); \
+ switch (coroParam->_line) { case 0:;
/**
* End the code section of a coroutine.
* @see CORO_END_CODE
*/
#define CORO_END_CODE \
- if (&coroParam == &Common::nullContext) { \
- delete Common::nullContext; \
- Common::nullContext = NULL; \
- } \
- }
+ if (&coroParam == &Common::nullContext) { \
+ delete Common::nullContext; \
+ Common::nullContext = NULL; \
+ } \
+ }
/**
* Sleep for the specified number of scheduler cycles.
*/
-#define CORO_SLEEP(delay) do {\
- coroParam->_line = __LINE__;\
- coroParam->_sleep = delay;\
- assert(&coroParam != &Common::nullContext);\
- return; case __LINE__:;\
- } while (0)
+#define CORO_SLEEP(delay) \
+ do { \
+ coroParam->_line = __LINE__; \
+ coroParam->_sleep = delay; \
+ assert(&coroParam != &Common::nullContext); \
+ return; case __LINE__:; \
+ } while (0)
#define CORO_GIVE_WAY do { CoroScheduler.giveWay(); CORO_SLEEP(1); } while (0)
#define CORO_RESCHEDULE do { CoroScheduler.reschedule(); CORO_SLEEP(1); } while (0)
@@ -174,7 +183,7 @@ public:
* then delete the entire coroutine's state, including all subcontexts).
*/
#define CORO_KILL_SELF() \
- do { if (&coroParam != &Common::nullContext) { coroParam->_sleep = -1; } return; } while (0)
+ do { if (&coroParam != &Common::nullContext) { coroParam->_sleep = -1; } return; } while (0)
/**
@@ -193,8 +202,8 @@ public:
* If the subcontext is null, the coroutine ended normally, and we can
* simply break out of the loop and continue execution.
*
- * @param subCoro name of the coroutine-enabled function to invoke
- * @param ARGS list of arguments to pass to subCoro
+ * @param subCoro name of the coroutine-enabled function to invoke
+ * @param ARGS list of arguments to pass to subCoro
*
* @note ARGS must be surrounded by parentheses, and the first argument
* in this list must always be CORO_SUBCTX. For example, the
@@ -203,18 +212,18 @@ public:
* becomes the following:
* CORO_INVOKE_ARGS(myFunc, (CORO_SUBCTX, a, b));
*/
-#define CORO_INVOKE_ARGS(subCoro, ARGS) \
- do {\
- coroParam->_line = __LINE__;\
- coroParam->_subctx = 0;\
- do {\
- subCoro ARGS;\
- if (!coroParam->_subctx) break;\
- coroParam->_sleep = coroParam->_subctx->_sleep;\
- assert(&coroParam != &Common::nullContext);\
- return; case __LINE__:;\
- } while (1);\
- } while (0)
+#define CORO_INVOKE_ARGS(subCoro, ARGS) \
+ do { \
+ coroParam->_line = __LINE__; \
+ coroParam->_subctx = 0; \
+ do { \
+ subCoro ARGS; \
+ if (!coroParam->_subctx) break; \
+ coroParam->_sleep = coroParam->_subctx->_sleep; \
+ assert(&coroParam != &Common::nullContext); \
+ return; case __LINE__:; \
+ } while (1); \
+ } while (0)
/**
* Invoke another coroutine. Similar to CORO_INVOKE_ARGS,
@@ -222,81 +231,82 @@ public:
* if invoked coroutine yields (thus causing the current
* coroutine to yield, too).
*/
-#define CORO_INVOKE_ARGS_V(subCoro, RESULT, ARGS) \
- do {\
- coroParam->_line = __LINE__;\
- coroParam->_subctx = 0;\
- do {\
- subCoro ARGS;\
- if (!coroParam->_subctx) break;\
- coroParam->_sleep = coroParam->_subctx->_sleep;\
- assert(&coroParam != &Common::nullContext);\
- return RESULT; case __LINE__:;\
- } while (1);\
- } while (0)
+#define CORO_INVOKE_ARGS_V(subCoro, RESULT, ARGS) \
+ do { \
+ coroParam->_line = __LINE__; \
+ coroParam->_subctx = 0; \
+ do { \
+ subCoro ARGS; \
+ if (!coroParam->_subctx) break; \
+ coroParam->_sleep = coroParam->_subctx->_sleep; \
+ assert(&coroParam != &Common::nullContext); \
+ return RESULT; case __LINE__:; \
+ } while (1); \
+ } while (0)
/**
* Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine
* with no parameters.
*/
#define CORO_INVOKE_0(subCoroutine) \
- CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX))
+ CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX))
/**
* Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine
* with one parameter.
*/
#define CORO_INVOKE_1(subCoroutine, a0) \
- CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0))
+ CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0))
/**
* Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine
* with two parameters.
*/
#define CORO_INVOKE_2(subCoroutine, a0,a1) \
- CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1))
+ CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1))
/**
* Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine
* with three parameters.
*/
#define CORO_INVOKE_3(subCoroutine, a0,a1,a2) \
- CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1,a2))
+ CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2))
/**
* Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine
* with four parameters.
*/
#define CORO_INVOKE_4(subCoroutine, a0,a1,a2,a3) \
- CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1,a2,a3))
+ CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2, a3))
// the size of process specific info
-#define CORO_PARAM_SIZE 32
+#define CORO_PARAM_SIZE 32
// the maximum number of processes
-#define CORO_NUM_PROCESS 100
-#define CORO_MAX_PROCESSES 100
+#define CORO_NUM_PROCESS 100
+#define CORO_MAX_PROCESSES 100
#define CORO_MAX_PID_WAITING 5
#define CORO_INFINITE 0xffffffff
#define CORO_INVALID_PID_VALUE 0
+/** Coroutine parameter for methods converted to coroutines */
typedef void (*CORO_ADDR)(CoroContext &, const void *);
/** process structure */
struct PROCESS {
- PROCESS *pNext; ///< pointer to next process in active or free list
- PROCESS *pPrevious; ///< pointer to previous process in active or free list
+ PROCESS *pNext; ///< pointer to next process in active or free list
+ PROCESS *pPrevious; ///< pointer to previous process in active or free list
- CoroContext state; ///< the state of the coroutine
- CORO_ADDR coroAddr; ///< the entry point of the coroutine
+ CoroContext state; ///< the state of the coroutine
+ CORO_ADDR coroAddr; ///< the entry point of the coroutine
- int sleepTime; ///< number of scheduler cycles to sleep
- uint32 pid; ///< process ID
- uint32 pidWaiting[CORO_MAX_PID_WAITING]; ///< Process ID(s) process is currently waiting on
- char param[CORO_PARAM_SIZE]; ///< process specific info
+ int sleepTime; ///< number of scheduler cycles to sleep
+ uint32 pid; ///< process ID
+ uint32 pidWaiting[CORO_MAX_PID_WAITING]; ///< Process ID(s) process is currently waiting on
+ char param[CORO_PARAM_SIZE]; ///< process specific info
};
typedef PROCESS *PPROCESS;
@@ -312,12 +322,24 @@ struct EVENT {
/**
* Creates and manages "processes" (really coroutines).
*/
-class CoroutineScheduler: public Singleton<CoroutineScheduler> {
+class CoroutineScheduler : public Singleton<CoroutineScheduler> {
public:
/** Pointer to a function of the form "void function(PPROCESS)" */
typedef void (*VFPTRPP)(PROCESS *);
private:
+ friend class Singleton<CoroutineScheduler>;
+
+ /**
+ * Constructor
+ */
+ CoroutineScheduler();
+
+ /**
+ * Destructor
+ */
+ ~CoroutineScheduler();
+
/** list of all processes */
PROCESS *processList;
@@ -342,7 +364,11 @@ private:
int numProcs;
int maxProcs;
- void CheckStack();
+ /**
+ * Checks both the active and free process list to insure all the links are valid,
+ * and that no processes have been lost
+ */
+ void checkStack();
#endif
/**
@@ -354,41 +380,175 @@ private:
PROCESS *getProcess(uint32 pid);
EVENT *getEvent(uint32 pid);
public:
-
- CoroutineScheduler();
- ~CoroutineScheduler();
-
+ /**
+ * Kills all processes and places them on the free list.
+ */
void reset();
- #ifdef DEBUG
+#ifdef DEBUG
+ /**
+ * Shows the maximum number of process used at once.
+ */
void printStats();
- #endif
+#endif
+ /**
+ * Give all active processes a chance to run
+ */
void schedule();
+
+ /**
+ * Reschedules all the processes to run again this tick
+ */
void rescheduleAll();
+
+ /**
+ * If the specified process has already run on this tick, make it run
+ * again on the current tick.
+ */
void reschedule(PPROCESS pReSchedProc = NULL);
+
+ /**
+ * Moves the specified process to the end of the dispatch queue
+ * allowing it to run again within the current game cycle.
+ * @param pGiveProc Which process
+ */
void giveWay(PPROCESS pReSchedProc = NULL);
+
+ /**
+ * Continously makes a given process wait for another process to finish or event to signal.
+ *
+ * @param pid Process/Event identifier
+ * @param duration Duration in milliseconds
+ * @param expired If specified, set to true if delay period expired
+ */
void waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool *expired = NULL);
- void waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, bool bWaitAll,
- uint32 duration, bool *expired = NULL);
+
+ /**
+ * Continously makes a given process wait for given prcesses to finished or events to be set
+ *
+ * @param nCount Number of Id's being passed
+ * @param evtList List of pids to wait for
+ * @param bWaitAll Specifies whether all or any of the processes/events
+ * @param duration Duration in milliseconds
+ * @param expired Set to true if delay period expired
+ */
+ void waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, bool bWaitAll,
+ uint32 duration, bool *expired = NULL);
+
+ /**
+ * Make the active process sleep for the given duration in milliseconds
+ *
+ * @param duration Duration in milliseconds
+ * @remarks This duration won't be precise, since it relies on the frequency the
+ * scheduler is called.
+ */
void sleep(CORO_PARAM, uint32 duration);
+ /**
+ * Creates a new process.
+ *
+ * @param pid process identifier
+ * @param coroAddr Coroutine start address
+ * @param pParam Process specific info
+ * @param sizeParam Size of process specific info
+ */
PROCESS *createProcess(uint32 pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam);
+
+ /**
+ * Creates a new process with an auto-incrementing Process Id.
+ *
+ * @param coroAddr Coroutine start address
+ * @param pParam Process specific info
+ * @param sizeParam Size of process specific info
+ */
uint32 createProcess(CORO_ADDR coroAddr, const void *pParam, int sizeParam);
+
+ /**
+ * Creates a new process with an auto-incrementing Process Id, and a single pointer parameter.
+ *
+ * @param coroAddr Coroutine start address
+ * @param pParam Process specific info
+ */
uint32 createProcess(CORO_ADDR coroAddr, const void *pParam);
+
+ /**
+ * Kills the specified process.
+ *
+ * @param pKillProc Which process to kill
+ */
void killProcess(PROCESS *pKillProc);
+ /**
+ * Returns a pointer to the currently running process.
+ */
PROCESS *getCurrentProcess();
+
+ /**
+ * Returns the process identifier of the currently running process.
+ */
int getCurrentPID() const;
+
+ /**
+ * Kills any process matching the specified PID. The current
+ * process cannot be killed.
+ *
+ * @param pidKill Process identifier of process to kill
+ * @param pidMask Mask to apply to process identifiers before comparison
+ * @return The number of processes killed is returned.
+ */
int killMatchingProcess(uint32 pidKill, int pidMask = -1);
+ /**
+ * Set pointer to a function to be called by killProcess().
+ *
+ * May be called by a resource allocator, the function supplied is
+ * called by killProcess() to allow the resource allocator to free
+ * resources allocated to the dying process.
+ *
+ * @param pFunc Function to be called by killProcess()
+ */
void setResourceCallback(VFPTRPP pFunc);
/* Event methods */
+ /**
+ * Creates a new event (semaphore) object
+ *
+ * @param bManualReset Events needs to be manually reset. Otherwise,
+ * events will be automatically reset after a
+ * process waits on the event finishes
+ * @param bInitialState Specifies whether the event is signalled or not
+ * initially
+ */
uint32 createEvent(bool bManualReset, bool bInitialState);
+
+ /**
+ * Destroys the given event
+ * @param pidEvent Event Process Id
+ */
void closeEvent(uint32 pidEvent);
+
+ /**
+ * Sets the event
+ * @param pidEvent Event Process Id
+ */
void setEvent(uint32 pidEvent);
+
+ /**
+ * Resets the event
+ * @param pidEvent Event Process Id
+ */
void resetEvent(uint32 pidEvent);
+
+ /**
+ * 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 Process Id
+ *
+ * @remarks Should not be run inside of another process
+ */
void pulseEvent(uint32 pidEvent);
};
@@ -396,4 +556,4 @@ public:
} // end of namespace Common
-#endif // COMMON_COROUTINES_H
+#endif // COMMON_COROUTINES_H
diff --git a/common/system.h b/common/system.h
index dc74533861..94bf7f01eb 100644
--- a/common/system.h
+++ b/common/system.h
@@ -380,33 +380,22 @@ public:
*
*
* The next layer is the overlay. It is composed over the game
- * graphics. By default, it has exactly the same size and
- * resolution as the game graphics. However, client code can
- * specify an overlay scale (as an additional parameter to
- * initSize()). This is meant to increase the resolution of the
- * overlay while keeping its size the same as that of the game
- * graphics. For example, if the overlay scale is 2, and the game
- * graphics have a resolution of 320x200; then the overlay shall
- * have a resolution of 640x400, but it still has the same
- * physical size as the game graphics.
- * The overlay usually uses 16bpp, but on some ports, only 8bpp
- * are availble, so that is supported, too, via a compile time
- * switch (see also the OverlayColor typedef in scummsys.h).
- *
+ * graphics. Historically the overlay size had always been a
+ * multiple of the game resolution, for example when the game
+ * resolution was 320x200 and the user selected a 2x scaler and did
+ * not enable aspect ratio correction it had a size of 640x400.
+ * An exception was the aspect ratio correction, which did allow
+ * for non multiples of the vertical resolution of the game screen.
+ * Nowadays the overlay size does not need to have any relation to
+ * the game resolution though, for example the overlay resolution
+ * might be the same as the physical screen resolution.
+ * The overlay is forced to a 16bpp mode right now.
*
* Finally, there is the mouse layer. This layer doesn't have to
* actually exist within the backend -- it all depends on how a
* backend chooses to implement mouse cursors, but in the default
* SDL backend, it really is a separate layer. The mouse can
* have a palette of its own, if the backend supports it.
- * The scale of the mouse cursor is called 'cursorTargetScale'.
- * This is meant as a hint to the backend. For example, let us
- * assume the overlay is not visible, and the game graphics are
- * displayed using a 2x scaler. If a mouse cursor with a
- * cursorTargetScale of 1 is set, then it should be scaled by
- * factor 2x, too, just like the game graphics. But if it has a
- * cursorTargetScale of 2, then it shouldn't be scaled again by
- * the game graphics scaler.
*
* On a note for OSystem users here. We do not require our graphics
* to be thread safe and in fact most/all backends using OpenGL are
@@ -758,13 +747,11 @@ public:
* In order to be able to display dialogs atop the game graphics, backends
* must provide an overlay mode.
*
- * The overlay can be 8 or 16 bpp. Depending on which it is, OverlayColor
- * is 8 or 16 bit.
+ * The overlay is currently forced at 16 bpp.
*
* For 'coolness' we usually want to have an overlay which is blended over
* the game graphics. On backends which support alpha blending, this is
- * no issue; but on other systems (in particular those which only support
- * 8bpp), this needs some trickery.
+ * no issue; but on other systems this needs some trickery.
*
* Essentially, we fake (alpha) blending on these systems by copying the
* current game graphics into the overlay buffer when activating the overlay,
@@ -883,10 +870,11 @@ public:
* @param keycolor transparency color value. This should not exceed the maximum color value of the specified format.
* In case it does the behavior is undefined. The backend might just error out or simply ignore the
* value. (The SDL backend will just assert to prevent abuse of this).
- * @param cursorTargetScale scale factor which cursor is designed for
+ * @param dontScale Whether the cursor should never be scaled. An exception are high ppi displays, where the cursor
+ * would be too small to notice otherwise, these are allowed to scale the cursor anyway.
* @param format pointer to the pixel format which cursor graphic uses (0 means CLUT8)
*/
- virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL) = 0;
+ virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL) = 0;
/**
* Replace the specified range of cursor the palette with new colors.
diff --git a/common/taskbar.h b/common/taskbar.h
index ba99d4e487..6f28028e74 100644
--- a/common/taskbar.h
+++ b/common/taskbar.h
@@ -34,7 +34,7 @@ namespace Common {
* The TaskbarManager allows interaction with the ScummVM application icon:
* - in the taskbar on Windows 7 and later
* - in the launcher for Unity
- * - in the dock on MacOSX
+ * - in the dock on Mac OS X
* - ...
*
* This allows GUI code and engines to display a progress bar, an overlay icon and/or count
diff --git a/common/updates.h b/common/updates.h
index 1e0babdf6d..4d58a216fb 100644
--- a/common/updates.h
+++ b/common/updates.h
@@ -30,7 +30,7 @@ namespace Common {
/**
* The UpdateManager allows configuring of the automatic update checking
* for systems that support it:
- * - using Sparkle on MacOSX
+ * - using Sparkle on Mac OS X
* - using WinSparkle on Windows
*
* Most of the update checking is completely automated and this class only