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
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (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, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/coroutines.h"
24 #include "common/algorithm.h"
25 #include "common/debug.h"
26 #include "common/hashmap.h"
27 #include "common/hash-str.h"
28 #include "common/system.h"
29 #include "common/textconsole.h"
30 
31 namespace Common {
32 
33 /** Helper null context instance */
34 CoroContext nullContext = nullptr;
35 
36 DECLARE_SINGLETON(CoroutineScheduler);
37 
38 #ifdef COROUTINE_DEBUG
39 namespace {
40 /** Count of active coroutines */
41 static int s_coroCount = 0;
42 
43 typedef Common::HashMap<Common::String, int> CoroHashMap;
44 static CoroHashMap *s_coroFuncs = 0;
45 
46 /**
47  * Change the current coroutine status
48  */
changeCoroStats(const char * func,int change)49 static void changeCoroStats(const char *func, int change) {
50 	if (!s_coroFuncs)
51 		s_coroFuncs = new CoroHashMap();
52 
53 	(*s_coroFuncs)[func] += change;
54 }
55 
56 /**
57  * Display the details of active coroutines
58  */
displayCoroStats()59 static void displayCoroStats() {
60 	debug("%d active coros", s_coroCount);
61 
62 	// Loop over s_coroFuncs and print info about active coros
63 	if (!s_coroFuncs)
64 		return;
65 	for (CoroHashMap::const_iterator it = s_coroFuncs->begin();
66 	        it != s_coroFuncs->end(); ++it) {
67 		if (it->_value != 0)
68 			debug("  %3d x %s", it->_value, it->_key.c_str());
69 	}
70 }
71 
72 } // End of anonymous namespace
73 #endif
74 
CoroBaseContext(const char * func)75 CoroBaseContext::CoroBaseContext(const char *func)
76 	: _line(0), _sleep(0), _subctx(nullptr) {
77 #ifdef COROUTINE_DEBUG
78 	_funcName = func;
79 	changeCoroStats(_funcName, +1);
80 	s_coroCount++;
81 #endif
82 }
83 
~CoroBaseContext()84 CoroBaseContext::~CoroBaseContext() {
85 #ifdef COROUTINE_DEBUG
86 	s_coroCount--;
87 	changeCoroStats(_funcName, -1);
88 	debug("Deleting coro in %s at %p (subctx %p)",
89 	      _funcName, (void *)this, (void *)_subctx);
90 	displayCoroStats();
91 #endif
92 	delete _subctx;
93 }
94 
95 //--------------------- Scheduler Class ------------------------
96 
CoroutineScheduler()97 CoroutineScheduler::CoroutineScheduler() {
98 	processList = nullptr;
99 	pFreeProcesses = nullptr;
100 	pCurrent = nullptr;
101 
102 #ifdef DEBUG
103 	// diagnostic process counters
104 	numProcs = 0;
105 	maxProcs = 0;
106 #endif
107 
108 	pRCfunction = nullptr;
109 	pidCounter = 0;
110 
111 	active = new PROCESS;
112 	active->pPrevious = nullptr;
113 	active->pNext = nullptr;
114 
115 	reset();
116 }
117 
~CoroutineScheduler()118 CoroutineScheduler::~CoroutineScheduler() {
119 	// Kill all running processes (i.e. free memory allocated for their state).
120 	PROCESS *pProc = active->pNext;
121 	while (pProc != nullptr) {
122 		delete pProc->state;
123 		pProc->state = nullptr;
124 		pProc = pProc->pNext;
125 	}
126 
127 	free(processList);
128 	processList = nullptr;
129 
130 	delete active;
131 	active = nullptr;
132 
133 	// Clear the event list
134 	Common::List<EVENT *>::iterator i;
135 	for (i = _events.begin(); i != _events.end(); ++i)
136 		delete *i;
137 }
138 
reset()139 void CoroutineScheduler::reset() {
140 #ifdef DEBUG
141 	// clear number of process in use
142 	numProcs = 0;
143 #endif
144 
145 	if (processList == nullptr) {
146 		// first time - allocate memory for process list
147 		processList = (PROCESS *)calloc(CORO_MAX_PROCESSES, sizeof(PROCESS));
148 
149 		// make sure memory allocated
150 		if (processList == nullptr) {
151 			error("Cannot allocate memory for process data");
152 		}
153 
154 		// fill with garbage
155 		memset(processList, 'S', CORO_MAX_PROCESSES * sizeof(PROCESS));
156 	}
157 
158 	// Kill all running processes (i.e. free memory allocated for their state).
159 	PROCESS *pProc = active->pNext;
160 	while (pProc != nullptr) {
161 		delete pProc->state;
162 		pProc->state = nullptr;
163 		Common::fill(&pProc->pidWaiting[0], &pProc->pidWaiting[CORO_MAX_PID_WAITING], 0);
164 		pProc = pProc->pNext;
165 	}
166 
167 	// no active processes
168 	pCurrent = active->pNext = nullptr;
169 
170 	// place first process on free list
171 	pFreeProcesses = processList;
172 
173 	// link all other processes after first
174 	for (int i = 1; i <= CORO_NUM_PROCESS; i++) {
175 		processList[i - 1].pNext = (i == CORO_NUM_PROCESS) ? nullptr : processList + i;
176 		processList[i - 1].pPrevious = (i == 1) ? active : processList + (i - 2);
177 	}
178 }
179 
180 
181 #ifdef DEBUG
printStats()182 void CoroutineScheduler::printStats() {
183 	debug("%i process of %i used", maxProcs, CORO_NUM_PROCESS);
184 }
185 #endif
186 
187 #ifdef DEBUG
checkStack()188 void CoroutineScheduler::checkStack() {
189 	Common::List<PROCESS *> pList;
190 
191 	// Check both the active and free process lists
192 	for (int i = 0; i < 2; ++i) {
193 		PROCESS *p = (i == 0) ? active : pFreeProcesses;
194 
195 		if (p != NULL) {
196 			// Make sure the linkages are correct
197 			while (p->pNext != NULL) {
198 				assert(p->pNext->pPrevious == p);
199 				pList.push_back(p);
200 				p = p->pNext;
201 			}
202 			pList.push_back(p);
203 		}
204 	}
205 
206 	// Make sure all processes are accounted for
207 	for (int idx = 0; idx < CORO_NUM_PROCESS; idx++) {
208 		bool found = false;
209 		for (Common::List<PROCESS *>::iterator i = pList.begin(); i != pList.end(); ++i) {
210 			PROCESS *pTemp = *i;
211 			if (*i == &processList[idx]) {
212 				found = true;
213 				break;
214 			}
215 		}
216 
217 		assert(found);
218 	}
219 }
220 #endif
221 
schedule()222 void CoroutineScheduler::schedule() {
223 	// start dispatching active process list
224 	PROCESS *pNext;
225 	PROCESS *pProc = active->pNext;
226 	while (pProc != nullptr) {
227 		pNext = pProc->pNext;
228 
229 		if (--pProc->sleepTime <= 0) {
230 			// process is ready for dispatch, activate it
231 			pCurrent = pProc;
232 			pProc->coroAddr(pProc->state, pProc->param);
233 
234 			if (!pProc->state || pProc->state->_sleep <= 0) {
235 				// Coroutine finished
236 				pCurrent = pCurrent->pPrevious;
237 				killProcess(pProc);
238 			} else {
239 				pProc->sleepTime = pProc->state->_sleep;
240 			}
241 
242 			// pCurrent may have been changed
243 			pNext = pCurrent->pNext;
244 			pCurrent = nullptr;
245 		}
246 
247 		pProc = pNext;
248 	}
249 
250 	// Disable any events that were pulsed
251 	Common::List<EVENT *>::iterator i;
252 	for (i = _events.begin(); i != _events.end(); ++i) {
253 		EVENT *evt = *i;
254 		if (evt->pulsing) {
255 			evt->pulsing = evt->signalled = false;
256 		}
257 	}
258 }
259 
rescheduleAll()260 void CoroutineScheduler::rescheduleAll() {
261 	assert(pCurrent);
262 
263 	// Unlink current process
264 	pCurrent->pPrevious->pNext = pCurrent->pNext;
265 	if (pCurrent->pNext)
266 		pCurrent->pNext->pPrevious = pCurrent->pPrevious;
267 
268 	// Add process to the start of the active list
269 	pCurrent->pNext = active->pNext;
270 	active->pNext->pPrevious = pCurrent;
271 	active->pNext = pCurrent;
272 	pCurrent->pPrevious = active;
273 }
274 
reschedule(PPROCESS pReSchedProc)275 void CoroutineScheduler::reschedule(PPROCESS pReSchedProc) {
276 	// If not currently processing the schedule list, then no action is needed
277 	if (!pCurrent)
278 		return;
279 
280 	if (!pReSchedProc)
281 		pReSchedProc = pCurrent;
282 
283 	PPROCESS pEnd;
284 
285 	// Find the last process in the list.
286 	// But if the target process is down the list from here, do nothing
287 	for (pEnd = pCurrent; pEnd->pNext != nullptr; pEnd = pEnd->pNext) {
288 		if (pEnd->pNext == pReSchedProc)
289 			return;
290 	}
291 
292 	assert(pEnd->pNext == nullptr);
293 
294 	// Could be in the middle of a KillProc()!
295 	// Dying process was last and this process was penultimate
296 	if (pReSchedProc->pNext == nullptr)
297 		return;
298 
299 	// If we're moving the current process, move it back by one, so that the next
300 	// schedule() iteration moves to the now next one
301 	if (pCurrent == pReSchedProc)
302 		pCurrent = pCurrent->pPrevious;
303 
304 	// Unlink the process, and add it at the end
305 	pReSchedProc->pPrevious->pNext = pReSchedProc->pNext;
306 	pReSchedProc->pNext->pPrevious = pReSchedProc->pPrevious;
307 	pEnd->pNext = pReSchedProc;
308 	pReSchedProc->pPrevious = pEnd;
309 	pReSchedProc->pNext = nullptr;
310 }
311 
giveWay(PPROCESS pReSchedProc)312 void CoroutineScheduler::giveWay(PPROCESS pReSchedProc) {
313 	// If not currently processing the schedule list, then no action is needed
314 	if (!pCurrent)
315 		return;
316 
317 	if (!pReSchedProc)
318 		pReSchedProc = pCurrent;
319 
320 	// If the process is already at the end of the queue, nothing has to be done
321 	if (!pReSchedProc->pNext)
322 		return;
323 
324 	PPROCESS pEnd;
325 
326 	// Find the last process in the list.
327 	for (pEnd = pCurrent; pEnd->pNext != nullptr; pEnd = pEnd->pNext)
328 		;
329 	assert(pEnd->pNext == nullptr);
330 
331 
332 	// If we're moving the current process, move it back by one, so that the next
333 	// schedule() iteration moves to the now next one
334 	if (pCurrent == pReSchedProc)
335 		pCurrent = pCurrent->pPrevious;
336 
337 	// Unlink the process, and add it at the end
338 	pReSchedProc->pPrevious->pNext = pReSchedProc->pNext;
339 	pReSchedProc->pNext->pPrevious = pReSchedProc->pPrevious;
340 	pEnd->pNext = pReSchedProc;
341 	pReSchedProc->pPrevious = pEnd;
342 	pReSchedProc->pNext = nullptr;
343 }
344 
waitForSingleObject(CORO_PARAM,int pid,uint32 duration,bool * expired)345 void CoroutineScheduler::waitForSingleObject(CORO_PARAM, int pid, uint32 duration, bool *expired) {
346 	if (!pCurrent)
347 		error("Called CoroutineScheduler::waitForSingleObject from the main process");
348 
349 	CORO_BEGIN_CONTEXT;
350 		uint32 endTime;
351 		PROCESS *pProcess;
352 		EVENT *pEvent;
353 	CORO_END_CONTEXT(_ctx);
354 
355 	CORO_BEGIN_CODE(_ctx);
356 
357 	// Signal the process Id this process is now waiting for
358 	pCurrent->pidWaiting[0] = pid;
359 
360 	_ctx->endTime = (duration == CORO_INFINITE) ? CORO_INFINITE : g_system->getMillis() + duration;
361 	if (expired)
362 		// Presume it will expire
363 		*expired = true;
364 
365 	// Outer loop for doing checks until expiry
366 	while (g_system->getMillis() <= _ctx->endTime) {
367 		// Check to see if a process or event with the given Id exists
368 		_ctx->pProcess = getProcess(pid);
369 		_ctx->pEvent = !_ctx->pProcess ? getEvent(pid) : nullptr;
370 
371 		// If there's no active process or event, presume it's a process that's finished,
372 		// so the waiting can immediately exit
373 		if ((_ctx->pProcess == nullptr) && (_ctx->pEvent == nullptr)) {
374 			if (expired)
375 				*expired = false;
376 			break;
377 		}
378 
379 		// If a process was found, don't go into the if statement, and keep waiting.
380 		// Likewise if it's an event that's not yet signalled
381 		if ((_ctx->pEvent != nullptr) && _ctx->pEvent->signalled) {
382 			// Unless the event is flagged for manual reset, reset it now
383 			if (!_ctx->pEvent->manualReset)
384 				_ctx->pEvent->signalled = false;
385 
386 			if (expired)
387 				*expired = false;
388 			break;
389 		}
390 
391 		// Sleep until the next cycle
392 		CORO_SLEEP(1);
393 	}
394 
395 	// Signal waiting is done
396 	Common::fill(&pCurrent->pidWaiting[0], &pCurrent->pidWaiting[CORO_MAX_PID_WAITING], 0);
397 
398 	CORO_END_CODE;
399 }
400 
waitForMultipleObjects(CORO_PARAM,int nCount,uint32 * pidList,bool bWaitAll,uint32 duration,bool * expired)401 void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 *pidList, bool bWaitAll,
402 												uint32 duration, bool *expired) {
403 	if (!pCurrent)
404 		error("Called CoroutineScheduler::waitForMultipleObjects from the main process");
405 
406 	CORO_BEGIN_CONTEXT;
407 		uint32 endTime;
408 		bool signalled;
409 		bool pidSignalled;
410 		int i;
411 		PROCESS *pProcess;
412 		EVENT *pEvent;
413 	CORO_END_CONTEXT(_ctx);
414 
415 	CORO_BEGIN_CODE(_ctx);
416 
417 	// Signal the waiting events
418 	assert(nCount < CORO_MAX_PID_WAITING);
419 	Common::copy(pidList, pidList + nCount, pCurrent->pidWaiting);
420 
421 	_ctx->endTime = (duration == CORO_INFINITE) ? CORO_INFINITE : g_system->getMillis() + duration;
422 	if (expired)
423 		// Presume that delay will expire
424 		*expired = true;
425 
426 	// Outer loop for doing checks until expiry
427 	while (g_system->getMillis() <= _ctx->endTime) {
428 		_ctx->signalled = bWaitAll;
429 
430 		for (_ctx->i = 0; _ctx->i < nCount; ++_ctx->i) {
431 			_ctx->pProcess = getProcess(pidList[_ctx->i]);
432 			_ctx->pEvent = !_ctx->pProcess ? getEvent(pidList[_ctx->i]) : nullptr;
433 
434 			// Determine the signalled state
435 			_ctx->pidSignalled = (_ctx->pProcess) || !_ctx->pEvent ? false : _ctx->pEvent->signalled;
436 
437 			if (bWaitAll && !_ctx->pidSignalled)
438 				_ctx->signalled = false;
439 			else if (!bWaitAll && _ctx->pidSignalled)
440 				_ctx->signalled = true;
441 		}
442 
443 		// At this point, if the signalled variable is set, waiting is finished
444 		if (_ctx->signalled) {
445 			// Automatically reset any events not flagged for manual reset
446 			for (_ctx->i = 0; _ctx->i < nCount; ++_ctx->i) {
447 				_ctx->pEvent = getEvent(pidList[_ctx->i]);
448 
449 				if (!_ctx->pEvent->manualReset)
450 					_ctx->pEvent->signalled = false;
451 			}
452 
453 			if (expired)
454 				*expired = false;
455 			break;
456 		}
457 
458 		// Sleep until the next cycle
459 		CORO_SLEEP(1);
460 	}
461 
462 	// Signal waiting is done
463 	Common::fill(&pCurrent->pidWaiting[0], &pCurrent->pidWaiting[CORO_MAX_PID_WAITING], 0);
464 
465 	CORO_END_CODE;
466 }
467 
sleep(CORO_PARAM,uint32 duration)468 void CoroutineScheduler::sleep(CORO_PARAM, uint32 duration) {
469 	if (!pCurrent)
470 		error("Called CoroutineScheduler::sleep from the main process");
471 
472 	CORO_BEGIN_CONTEXT;
473 		uint32 endTime;
474 		PROCESS *pProcess;
475 		EVENT *pEvent;
476 	CORO_END_CONTEXT(_ctx);
477 
478 	CORO_BEGIN_CODE(_ctx);
479 
480 	_ctx->endTime = g_system->getMillis() + duration;
481 
482 	// Outer loop for doing checks until expiry
483 	while (g_system->getMillis() < _ctx->endTime) {
484 		// Sleep until the next cycle
485 		CORO_SLEEP(1);
486 	}
487 
488 	CORO_END_CODE;
489 }
490 
createProcess(uint32 pid,CORO_ADDR coroAddr,const void * pParam,int sizeParam)491 PROCESS *CoroutineScheduler::createProcess(uint32 pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam) {
492 	PROCESS *pProc;
493 
494 	// get a free process
495 	pProc = pFreeProcesses;
496 
497 	// trap no free process
498 	assert(pProc != nullptr); // Out of processes
499 
500 #ifdef DEBUG
501 	// one more process in use
502 	if (++numProcs > maxProcs)
503 		maxProcs = numProcs;
504 #endif
505 
506 	// get link to next free process
507 	pFreeProcesses = pProc->pNext;
508 	if (pFreeProcesses)
509 		pFreeProcesses->pPrevious = nullptr;
510 
511 	if (pCurrent != nullptr) {
512 		// place new process before the next active process
513 		pProc->pNext = pCurrent->pNext;
514 		if (pProc->pNext)
515 			pProc->pNext->pPrevious = pProc;
516 
517 		// make this new process the next active process
518 		pCurrent->pNext = pProc;
519 		pProc->pPrevious = pCurrent;
520 
521 	} else { // no active processes, place process at head of list
522 		pProc->pNext = active->pNext;
523 		pProc->pPrevious = active;
524 
525 		if (pProc->pNext)
526 			pProc->pNext->pPrevious = pProc;
527 		active->pNext = pProc;
528 
529 	}
530 
531 	// set coroutine entry point
532 	pProc->coroAddr = coroAddr;
533 
534 	// clear coroutine state
535 	pProc->state = nullptr;
536 
537 	// wake process up as soon as possible
538 	pProc->sleepTime = 1;
539 
540 	// set new process id
541 	pProc->pid = pid;
542 
543 	// set new process specific info
544 	if (sizeParam) {
545 		assert(sizeParam > 0 && sizeParam <= CORO_PARAM_SIZE);
546 
547 		// set new process specific info
548 		memcpy(pProc->param, pParam, sizeParam);
549 	}
550 
551 	// return created process
552 	return pProc;
553 }
554 
createProcess(CORO_ADDR coroAddr,const void * pParam,int sizeParam)555 uint32 CoroutineScheduler::createProcess(CORO_ADDR coroAddr, const void *pParam, int sizeParam) {
556 	PROCESS *pProc = createProcess(++pidCounter, coroAddr, pParam, sizeParam);
557 	return pProc->pid;
558 }
559 
createProcess(CORO_ADDR coroAddr,const void * pParam)560 uint32 CoroutineScheduler::createProcess(CORO_ADDR coroAddr, const void *pParam) {
561 	return createProcess(coroAddr, &pParam, sizeof(void *));
562 }
563 
killProcess(PROCESS * pKillProc)564 void CoroutineScheduler::killProcess(PROCESS *pKillProc) {
565 	// make sure a valid process pointer
566 	assert(pKillProc >= processList && pKillProc <= processList + CORO_NUM_PROCESS - 1);
567 
568 	// can not kill the current process using killProcess !
569 	assert(pCurrent != pKillProc);
570 
571 #ifdef DEBUG
572 	// one less process in use
573 	--numProcs;
574 	assert(numProcs >= 0);
575 #endif
576 
577 	// Free process' resources
578 	if (pRCfunction != nullptr)
579 		(pRCfunction)(pKillProc);
580 
581 	delete pKillProc->state;
582 	pKillProc->state = nullptr;
583 
584 	// Take the process out of the active chain list
585 	pKillProc->pPrevious->pNext = pKillProc->pNext;
586 	if (pKillProc->pNext)
587 		pKillProc->pNext->pPrevious = pKillProc->pPrevious;
588 
589 	// link first free process after pProc
590 	pKillProc->pNext = pFreeProcesses;
591 	if (pFreeProcesses)
592 		pKillProc->pNext->pPrevious = pKillProc;
593 	pKillProc->pPrevious = nullptr;
594 
595 	// make pKillProc the first free process
596 	pFreeProcesses = pKillProc;
597 }
598 
getCurrentProcess()599 PROCESS *CoroutineScheduler::getCurrentProcess() {
600 	return pCurrent;
601 }
602 
getCurrentPID() const603 int CoroutineScheduler::getCurrentPID() const {
604 	PROCESS *pProc = pCurrent;
605 
606 	// make sure a valid process pointer
607 	assert(pProc >= processList && pProc <= processList + CORO_NUM_PROCESS - 1);
608 
609 	// return processes PID
610 	return pProc->pid;
611 }
612 
killMatchingProcess(uint32 pidKill,int pidMask)613 int CoroutineScheduler::killMatchingProcess(uint32 pidKill, int pidMask) {
614 	int numKilled = 0;
615 	PROCESS *pProc, *pPrev; // process list pointers
616 
617 	for (pProc = active->pNext, pPrev = active; pProc != nullptr; pPrev = pProc, pProc = pProc->pNext) {
618 		if ((pProc->pid & (uint32)pidMask) == pidKill) {
619 			// found a matching process
620 
621 			// dont kill the current process
622 			if (pProc != pCurrent) {
623 				// kill this process
624 				numKilled++;
625 
626 				// Free the process' resources
627 				if (pRCfunction != nullptr)
628 					(pRCfunction)(pProc);
629 
630 				delete pProc->state;
631 				pProc->state = nullptr;
632 
633 				// make prev point to next to unlink pProc
634 				pPrev->pNext = pProc->pNext;
635 				if (pProc->pNext)
636 					pPrev->pNext->pPrevious = pPrev;
637 
638 				// link first free process after pProc
639 				pProc->pNext = pFreeProcesses;
640 				pProc->pPrevious = nullptr;
641 				pFreeProcesses->pPrevious = pProc;
642 
643 				// make pProc the first free process
644 				pFreeProcesses = pProc;
645 
646 				// set to a process on the active list
647 				pProc = pPrev;
648 			}
649 		}
650 	}
651 
652 #ifdef DEBUG
653 	// adjust process in use
654 	numProcs -= numKilled;
655 	assert(numProcs >= 0);
656 #endif
657 
658 	// return number of processes killed
659 	return numKilled;
660 }
661 
setResourceCallback(VFPTRPP pFunc)662 void CoroutineScheduler::setResourceCallback(VFPTRPP pFunc) {
663 	pRCfunction = pFunc;
664 }
665 
getProcess(uint32 pid)666 PROCESS *CoroutineScheduler::getProcess(uint32 pid) {
667 	PROCESS *pProc = active->pNext;
668 	while ((pProc != nullptr) && (pProc->pid != pid))
669 		pProc = pProc->pNext;
670 
671 	return pProc;
672 }
673 
getEvent(uint32 pid)674 EVENT *CoroutineScheduler::getEvent(uint32 pid) {
675 	Common::List<EVENT *>::iterator i;
676 	for (i = _events.begin(); i != _events.end(); ++i) {
677 		EVENT *evt = *i;
678 		if (evt->pid == pid)
679 			return evt;
680 	}
681 
682 	return nullptr;
683 }
684 
685 
createEvent(bool bManualReset,bool bInitialState)686 uint32 CoroutineScheduler::createEvent(bool bManualReset, bool bInitialState) {
687 	EVENT *evt = new EVENT();
688 	evt->pid = ++pidCounter;
689 	evt->manualReset = bManualReset;
690 	evt->signalled = bInitialState;
691 	evt->pulsing = false;
692 
693 	_events.push_back(evt);
694 	return evt->pid;
695 }
696 
closeEvent(uint32 pidEvent)697 void CoroutineScheduler::closeEvent(uint32 pidEvent) {
698 	EVENT *evt = getEvent(pidEvent);
699 	if (evt) {
700 		_events.remove(evt);
701 		delete evt;
702 	}
703 }
704 
setEvent(uint32 pidEvent)705 void CoroutineScheduler::setEvent(uint32 pidEvent) {
706 	EVENT *evt = getEvent(pidEvent);
707 	if (evt)
708 		evt->signalled = true;
709 }
710 
resetEvent(uint32 pidEvent)711 void CoroutineScheduler::resetEvent(uint32 pidEvent) {
712 	EVENT *evt = getEvent(pidEvent);
713 	if (evt)
714 		evt->signalled = false;
715 }
716 
pulseEvent(uint32 pidEvent)717 void CoroutineScheduler::pulseEvent(uint32 pidEvent) {
718 	EVENT *evt = getEvent(pidEvent);
719 	if (!evt)
720 		return;
721 
722 	// Set the event as signalled and pulsing
723 	evt->signalled = true;
724 	evt->pulsing = true;
725 
726 	// If there's an active process, and it's not the first in the queue, then reschedule all
727 	// the other prcoesses in the queue to run again this frame
728 	if (pCurrent && pCurrent != active->pNext)
729 		rescheduleAll();
730 }
731 
732 } // end of namespace Common
733