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 = NULL;
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(0) {
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 = NULL;
99 pFreeProcesses = NULL;
100 pCurrent = NULL;
101
102 #ifdef DEBUG
103 // diagnostic process counters
104 numProcs = 0;
105 maxProcs = 0;
106 #endif
107
108 pRCfunction = NULL;
109 pidCounter = 0;
110
111 active = new PROCESS;
112 active->pPrevious = NULL;
113 active->pNext = NULL;
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 != NULL) {
122 delete pProc->state;
123 pProc->state = 0;
124 pProc = pProc->pNext;
125 }
126
127 free(processList);
128 processList = NULL;
129
130 delete active;
131 active = 0;
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 == NULL) {
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 == NULL) {
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 != NULL) {
161 delete pProc->state;
162 pProc->state = 0;
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 = NULL;
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) ? NULL : 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 != NULL) {
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 = NULL;
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 != NULL; pEnd = pEnd->pNext) {
288 if (pEnd->pNext == pReSchedProc)
289 return;
290 }
291
292 assert(pEnd->pNext == NULL);
293
294 // Could be in the middle of a KillProc()!
295 // Dying process was last and this process was penultimate
296 if (pReSchedProc->pNext == NULL)
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 = NULL;
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 != NULL; pEnd = pEnd->pNext)
328 ;
329 assert(pEnd->pNext == NULL);
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 = NULL;
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) : NULL;
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 == NULL) && (_ctx->pEvent == NULL)) {
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 != NULL) && _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]) : NULL;
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 != NULL); // 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 = NULL;
510
511 if (pCurrent != NULL) {
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 = 0;
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 != NULL)
579 (pRCfunction)(pKillProc);
580
581 delete pKillProc->state;
582 pKillProc->state = 0;
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 = NULL;
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 != NULL; 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 != NULL)
628 (pRCfunction)(pProc);
629
630 delete pProc->state;
631 pProc->state = 0;
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 = NULL;
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 != NULL) && (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 NULL;
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