ScummVM API documentation
Coroutine support for simulating multi-threading

Description

The following implementation is loosely based on an article by Simon Tatham: Coroutines in C. However, many improvements and tweaks have been made, in particular by taking advantage of C++ features not available in C.

Classes

struct  Common::CoroBaseContext
 
class  Common::CoroContextHolder
 
struct  Common::PROCESS
 
struct  Common::EVENT
 
class  Common::CoroutineScheduler
 

Macros

#define CoroScheduler   (Common::CoroutineScheduler::instance())
 
#define CORO_PARAM   Common::CoroContext &coroParam
 
#define CORO_BEGIN_CONTEXT
 
#define CORO_END_CONTEXT(x)   } *x = (CoroContextTag *)coroParam
 
#define CORO_BEGIN_CODE(x)
 
#define CORO_END_CODE
 
#define CORO_SLEEP(delay)
 
#define CORO_GIVE_WAY   do { CoroScheduler.giveWay(); CORO_SLEEP(1); } while (0)
 
#define CORO_RESCHEDULE   do { CoroScheduler.reschedule(); CORO_SLEEP(1); } while (0)
 
#define CORO_KILL_SELF()   do { if (&coroParam != &Common::nullContext) { coroParam->_sleep = -1; } return; } while (0)
 
#define CORO_SUBCTX   coroParam->_subctx
 
#define CORO_INVOKE_ARGS(subCoro, ARGS)
 
#define CORO_INVOKE_ARGS_V(subCoro, RESULT, ARGS)
 
#define CORO_INVOKE_0(subCoroutine)   CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX))
 
#define CORO_INVOKE_1(subCoroutine, a0)   CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0))
 
#define CORO_INVOKE_2(subCoroutine, a0, a1)   CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1))
 
#define CORO_INVOKE_3(subCoroutine, a0, a1, a2)   CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2))
 
#define CORO_INVOKE_4(subCoroutine, a0, a1, a2, a3)   CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2, a3))
 
#define CORO_PARAM_SIZE   40
 
#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
 

Typedefs

typedef CoroBaseContextCommon::CoroContext
 
typedef void(* Common::CORO_ADDR) (CoroContext &, const void *)
 
typedef PROCESSCommon::PPROCESS
 

Variables

CoroContext Common::nullContext
 

Macro Definition Documentation

◆ CORO_PARAM

#define CORO_PARAM   Common::CoroContext &coroParam

Set this as the first parameter for methods that have been converted to being a coroutine.

◆ CORO_BEGIN_CONTEXT

#define CORO_BEGIN_CONTEXT
Value:
struct CoroContextTag : Common::CoroBaseContext { \
CoroContextTag() : CoroBaseContext(SCUMMVM_CURRENT_FUNCTION) { DUMMY = 0; } \
int DUMMY
Definition: coroutines.h:54
#define SCUMMVM_CURRENT_FUNCTION
Definition: util.h:118

Begin the declaration of a coroutine context. This allows declaring variables which are 'persistent' during the lifetime of the coroutine. Example usage:

int var;
char *foo;

It is not possible to initialize variables here, due to the way this macro is implemented. Furthermore, to use the variables declared in the coroutine context, you must access them through the context variable name that was specified as a parameter to CORO_END_CONTEXT, e.g. _ctx->var = 0;

See also
CORO_END_CONTEXT
Note
A 'DUMMY' variable is declared to allow the user to specify an 'empty' context, and so that compilers do not complain about ";" following the macro.

◆ CORO_END_CONTEXT

#define CORO_END_CONTEXT (   x)    } *x = (CoroContextTag *)coroParam

End the declaration of a coroutine context.

Parameters
xName of the coroutine context.
See also
CORO_BEGIN_CONTEXT

◆ CORO_BEGIN_CODE

#define CORO_BEGIN_CODE (   x)
Value:
if (&coroParam == &Common::nullContext) assert(!Common::nullContext); \
if (!x) { coroParam = x = new CoroContextTag(); } \
x->DUMMY = 0; \
Common::CoroContextHolder tmpHolder(coroParam); \
switch (coroParam->_line) { default: break; case 0:;
CoroContext nullContext

Begin the code section of a coroutine.

Parameters
xName of the coroutine context.

◆ CORO_END_CODE

#define CORO_END_CODE
Value:
if (&coroParam == &Common::nullContext) { \
} \
}
CoroContext nullContext

End the code section of a coroutine.

◆ CORO_SLEEP

#define CORO_SLEEP (   delay)
Value:
do { \
coroParam->_line = __LINE__; \
coroParam->_sleep = delay; \
assert(&coroParam != &Common::nullContext); \
return; case __LINE__:; \
} while (0)
CoroContext nullContext

Sleep for the specified number of scheduler cycles.

◆ CORO_KILL_SELF

#define CORO_KILL_SELF ( )    do { if (&coroParam != &Common::nullContext) { coroParam->_sleep = -1; } return; } while (0)

Stop the currently running coroutine and all calling coroutines.

This sets _sleep to -1 rather than 0 so that the context does not get deleted by CoroContextHolder, since we want CORO_INVOKE_ARGS to propagate the _sleep value and return immediately (the scheduler will then delete the entire coroutine's state, including all subcontexts).

◆ CORO_SUBCTX

#define CORO_SUBCTX   coroParam->_subctx

Use this macro in conjunction with CORO_INVOKE_ARGS and similar macros for calling coroutine-enabled subroutines.

◆ CORO_INVOKE_ARGS

#define CORO_INVOKE_ARGS (   subCoro,
  ARGS 
)
Value:
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)
CoroContext nullContext
Definition: algorithm.h:29

Invoke another coroutine.

If the subcontext still exists after the coroutine is invoked, it has either yielded/slept or killed itself, and so we copy the _sleep value to our own context and return (execution will continue at the case statement below, where we loop and call the coroutine again). If the subcontext is null, the coroutine ended normally, and we can simply break out of the loop and continue execution.

Parameters
subCoroName of the coroutine-enabled function to invoke.
ARGSList 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 regular function call myFunc(a, b); becomes the following: CORO_INVOKE_ARGS(myFunc, (CORO_SUBCTX, a, b));

◆ CORO_INVOKE_ARGS_V

#define CORO_INVOKE_ARGS_V (   subCoro,
  RESULT,
  ARGS 
)
Value:
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)
CoroContext nullContext
Definition: algorithm.h:29

Invoke another coroutine. Similar to CORO_INVOKE_ARGS, but allows specifying a return value which is returned if the invoked coroutine yields (thus causing the current coroutine to yield, too).

◆ CORO_INVOKE_0

#define CORO_INVOKE_0 (   subCoroutine)    CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX))

Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with no parameters.

◆ CORO_INVOKE_1

#define CORO_INVOKE_1 (   subCoroutine,
  a0 
)    CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0))

Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with one parameter.

◆ CORO_INVOKE_2

#define CORO_INVOKE_2 (   subCoroutine,
  a0,
  a1 
)    CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1))

Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with two parameters.

◆ CORO_INVOKE_3

#define CORO_INVOKE_3 (   subCoroutine,
  a0,
  a1,
  a2 
)    CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2))

Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with three parameters.

◆ CORO_INVOKE_4

#define CORO_INVOKE_4 (   subCoroutine,
  a0,
  a1,
  a2,
  a3 
)    CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2, a3))

Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with four parameters.

◆ CORO_PARAM_SIZE

#define CORO_PARAM_SIZE   40

Size of process-specific information.

◆ CORO_NUM_PROCESS

#define CORO_NUM_PROCESS   100

Maximum number of processes.

Typedef Documentation

◆ CORO_ADDR

typedef void(* Common::CORO_ADDR) (CoroContext &, const void *)

Coroutine parameter for methods converted to coroutines.

Variable Documentation

◆ nullContext

CoroContext Common::nullContext

This is a special constant that can be temporarily used as a parameter to call coroutine-ised methods from code that have not 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.