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.
|
#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 |
|
◆ CORO_PARAM
Set this as the first parameter for methods that have been converted to being a coroutine.
◆ CORO_BEGIN_CONTEXT
#define CORO_BEGIN_CONTEXT |
Value: 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:
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
-
x | Name of the coroutine context. |
- See also
- CORO_BEGIN_CONTEXT
◆ CORO_BEGIN_CODE
#define CORO_BEGIN_CODE |
( |
|
x | ) |
|
Value: if (!x) { coroParam = x = new CoroContextTag(); } \
x->DUMMY = 0; \
Common::CoroContextHolder tmpHolder(coroParam); \
switch (coroParam->_line) { default: break; case 0:;
Begin the code section of a coroutine.
- Parameters
-
x | Name of the coroutine context. |
◆ CORO_END_CODE
Value:End the code section of a coroutine.
◆ CORO_SLEEP
#define CORO_SLEEP |
( |
|
delay | ) |
|
Value:do { \
coroParam->_line = __LINE__; \
coroParam->_sleep = delay; \
return; case __LINE__:; \
} while (0)
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; \
return; case __LINE__:; \
} while (1); \
} while (0)
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
-
subCoro | Name of the coroutine-enabled function to invoke. |
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 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; \
return RESULT; case __LINE__:; \
} while (1); \
} while (0)
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
Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with no parameters.
◆ CORO_INVOKE_1
Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with one parameter.
◆ CORO_INVOKE_2
Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with two parameters.
◆ CORO_INVOKE_3
Convenience wrapper for CORO_INVOKE_ARGS for invoking a coroutine with three parameters.
◆ CORO_INVOKE_4
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.
◆ CORO_ADDR
typedef void(* Common::CORO_ADDR) (CoroContext &, const void *) |
Coroutine parameter for methods converted to coroutines.
◆ 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.