ScummVM API documentation
coroutines.h
1/* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef COMMON_COROUTINES_H
23#define COMMON_COROUTINES_H
24
25#include "common/scummsys.h"
26#include "common/util.h" // for SCUMMVM_CURRENT_FUNCTION
27#include "common/list.h"
28#include "common/singleton.h"
29
30namespace Common {
31
44#define CoroScheduler (Common::CoroutineScheduler::instance())
45
46
47// Enable this macro to enable some debugging support in the coroutine code.
48//#define COROUTINE_DEBUG
49
55 int _line;
56 int _sleep;
57 CoroBaseContext *_subctx;
58#ifdef COROUTINE_DEBUG
59 const char *_funcName;
60#endif
64 CoroBaseContext(const char *func);
65
70};
71
73
74
81
91 CoroContext &_ctx;
92public:
93 CoroContextHolder(CoroContext &ctx) : _ctx(ctx) {
94 assert(ctx);
95 assert(ctx->_sleep >= 0);
96 ctx->_sleep = 0;
97 }
99 if (_ctx && _ctx->_sleep == 0) {
100 delete _ctx;
101 _ctx = nullptr;
102 }
103 }
104};
105
107#define CORO_PARAM Common::CoroContext &coroParam
108
109
131#define CORO_BEGIN_CONTEXT \
132 struct CoroContextTag : Common::CoroBaseContext { \
133 CoroContextTag() : CoroBaseContext(SCUMMVM_CURRENT_FUNCTION) { DUMMY = 0; } \
134 int DUMMY
135
141#define CORO_END_CONTEXT(x) } *x = (CoroContextTag *)coroParam
142
147#define CORO_BEGIN_CODE(x) \
148 if (&coroParam == &Common::nullContext) assert(!Common::nullContext); \
149 if (!x) { coroParam = x = new CoroContextTag(); } \
150 x->DUMMY = 0; \
151 Common::CoroContextHolder tmpHolder(coroParam); \
152 switch (coroParam->_line) { default: break; case 0:;
153
157#define CORO_END_CODE \
158 if (&coroParam == &Common::nullContext) { \
159 delete Common::nullContext; \
160 Common::nullContext = NULL; \
161 } \
162 }
163
167#define CORO_SLEEP(delay) \
168 do { \
169 coroParam->_line = __LINE__; \
170 coroParam->_sleep = delay; \
171 assert(&coroParam != &Common::nullContext); \
172 return; case __LINE__:; \
173 } while (0)
174
175#define CORO_GIVE_WAY do { CoroScheduler.giveWay(); CORO_SLEEP(1); } while (0)
176#define CORO_RESCHEDULE do { CoroScheduler.reschedule(); CORO_SLEEP(1); } while (0)
177
186#define CORO_KILL_SELF() \
187 do { if (&coroParam != &Common::nullContext) { coroParam->_sleep = -1; } return; } while (0)
188
189
194#define CORO_SUBCTX coroParam->_subctx
195
216#define CORO_INVOKE_ARGS(subCoro, ARGS) \
217 do { \
218 coroParam->_line = __LINE__; \
219 coroParam->_subctx = 0; \
220 do { \
221 subCoro ARGS; \
222 if (!coroParam->_subctx) break; \
223 coroParam->_sleep = coroParam->_subctx->_sleep; \
224 assert(&coroParam != &Common::nullContext); \
225 return; case __LINE__:; \
226 } while (1); \
227 } while (0)
228
235#define CORO_INVOKE_ARGS_V(subCoro, RESULT, ARGS) \
236 do { \
237 coroParam->_line = __LINE__; \
238 coroParam->_subctx = 0; \
239 do { \
240 subCoro ARGS; \
241 if (!coroParam->_subctx) break; \
242 coroParam->_sleep = coroParam->_subctx->_sleep; \
243 assert(&coroParam != &Common::nullContext); \
244 return RESULT; case __LINE__:; \
245 } while (1); \
246 } while (0)
247
252#define CORO_INVOKE_0(subCoroutine) \
253 CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX))
254
259#define CORO_INVOKE_1(subCoroutine, a0) \
260 CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0))
261
266#define CORO_INVOKE_2(subCoroutine, a0,a1) \
267 CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1))
268
273#define CORO_INVOKE_3(subCoroutine, a0,a1,a2) \
274 CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2))
275
280#define CORO_INVOKE_4(subCoroutine, a0,a1,a2,a3) \
281 CORO_INVOKE_ARGS(subCoroutine, (CORO_SUBCTX, a0, a1, a2, a3))
282
283
284
286#define CORO_PARAM_SIZE 40
287
289#define CORO_NUM_PROCESS 100
290#define CORO_MAX_PROCESSES 100
291#define CORO_MAX_PID_WAITING 5
292
293#define CORO_INFINITE 0xffffffff
294#define CORO_INVALID_PID_VALUE 0
295
297typedef void (*CORO_ADDR)(CoroContext &, const void *);
298
300struct PROCESS {
303
306
308 uint32 pid;
309 uint32 pidWaiting[CORO_MAX_PID_WAITING];
310#ifndef NO_CXX11_ALIGNAS
311 alignas(max_align_t)
312#endif
314};
315typedef PROCESS *PPROCESS;
316
317
319struct EVENT {
320 uint32 pid;
321 bool manualReset;
322 bool signalled;
323 bool pulsing;
324};
325
326
330class CoroutineScheduler : public Singleton<CoroutineScheduler> {
331public:
333 typedef void (*VFPTRPP)(PROCESS *);
334
335private:
336 friend class Singleton<CoroutineScheduler>;
337
342
347
348
350 PROCESS *processList;
351
353 PROCESS *active;
354
356 PROCESS *pFreeProcesses;
357
359 PROCESS *pCurrent;
360
362 int pidCounter;
363
365 Common::List<EVENT *> _events;
366
367#ifdef DEBUG
369 int numProcs;
370 int maxProcs;
371
376 void checkStack();
377#endif
378
383 VFPTRPP pRCfunction;
384
385 PROCESS *getProcess(uint32 pid);
386 EVENT *getEvent(uint32 pid);
387public:
391 void reset();
392
393#ifdef DEBUG
397 void printStats();
398#endif
399
403 void schedule();
404
409
414 void reschedule(PPROCESS pReSchedProc = nullptr);
415
421 void giveWay(PPROCESS pReSchedProc = nullptr);
422
430 void waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool *expired = nullptr);
431
441 void waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, bool bWaitAll,
442 uint32 duration, bool *expired = nullptr);
443
451 void sleep(CORO_PARAM, uint32 duration);
452
461 PROCESS *createProcess(uint32 pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam);
462
470 uint32 createProcess(CORO_ADDR coroAddr, const void *pParam, int sizeParam);
471
478 uint32 createProcess(CORO_ADDR coroAddr, const void *pParam);
479
485 void killProcess(PROCESS *pKillProc);
486
491
495 int getCurrentPID() const;
496
505 int killMatchingProcess(uint32 pidKill, int pidMask = -1);
506
517
530 uint32 createEvent(bool bManualReset, bool bInitialState);
531
536 void closeEvent(uint32 pidEvent);
537
542 void setEvent(uint32 pidEvent);
543
548 void resetEvent(uint32 pidEvent);
549
559 void pulseEvent(uint32 pidEvent);
560};
561
566} // end of namespace Common
567
568#endif // COMMON_COROUTINES_H
Definition: coroutines.h:90
Definition: coroutines.h:330
void(* VFPTRPP)(PROCESS *)
Definition: coroutines.h:333
void resetEvent(uint32 pidEvent)
int killMatchingProcess(uint32 pidKill, int pidMask=-1)
uint32 createEvent(bool bManualReset, bool bInitialState)
PROCESS * createProcess(uint32 pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam)
void pulseEvent(uint32 pidEvent)
void setEvent(uint32 pidEvent)
uint32 createProcess(CORO_ADDR coroAddr, const void *pParam, int sizeParam)
void closeEvent(uint32 pidEvent)
void reschedule(PPROCESS pReSchedProc=nullptr)
void killProcess(PROCESS *pKillProc)
void sleep(Common::CoroContext &coroParam, uint32 duration)
void giveWay(PPROCESS pReSchedProc=nullptr)
void waitForMultipleObjects(Common::CoroContext &coroParam, int nCount, uint32 *pidList, bool bWaitAll, uint32 duration, bool *expired=nullptr)
uint32 createProcess(CORO_ADDR coroAddr, const void *pParam)
void setResourceCallback(VFPTRPP pFunc)
void waitForSingleObject(Common::CoroContext &coroParam, int pid, uint32 duration, bool *expired=nullptr)
Definition: list.h:44
Definition: singleton.h:42
#define CORO_PARAM
Definition: coroutines.h:107
#define CORO_PARAM_SIZE
Definition: coroutines.h:286
CoroContext nullContext
void(* CORO_ADDR)(CoroContext &, const void *)
Definition: coroutines.h:297
Definition: algorithm.h:29
Definition: coroutines.h:54
CoroBaseContext(const char *func)
Definition: coroutines.h:319
Definition: coroutines.h:300
CORO_ADDR coroAddr
Entry point of the coroutine.
Definition: coroutines.h:305
char param[40]
Process-specific information.
Definition: coroutines.h:313
PROCESS * pPrevious
Pointer to the previous process in an active or free list.
Definition: coroutines.h:302
int sleepTime
Number of scheduler cycles to sleep.
Definition: coroutines.h:307
uint32 pidWaiting[5]
Process ID(s) that the process is currently waiting on.
Definition: coroutines.h:309
uint32 pid
Process ID.
Definition: coroutines.h:308
CoroContext state
State of the coroutine.
Definition: coroutines.h:304
PROCESS * pNext
Pointer to the next process in an active or free list.
Definition: coroutines.h:301