comparison AnimationMaster.c @ 260:999f2966a3e5

new branch -- Dev_ML -- for making VMS take langlets whose constructs can be mixed
author Sean Halle <seanhalle@yahoo.com>
date Wed, 19 Sep 2012 23:12:44 -0700
parents 7ed97c961901
children dafae55597ce
comparison
equal deleted inserted replaced
7:0145129a40f5 9:f93b7a95183a
7 7
8 8
9 #include <stdio.h> 9 #include <stdio.h>
10 #include <stddef.h> 10 #include <stddef.h>
11 11
12 #include "VMS.h" 12 #include "PR.h"
13 13
14 14
15 15
16 /*The animationMaster embodies most of the animator of the language. The 16 /*The animationMaster embodies most of the animator of the language. The
17 * animator is what emodies the behavior of language constructs. 17 * animator is what emodies the behavior of language constructs.
18 * As such, it is the animationMaster, in combination with the plugin 18 * As such, it is the animationMaster, in combination with the plugin
19 * functions, that make the language constructs do their behavior. 19 * functions, that make the language constructs do their behavior.
20 * 20 *
21 *Within the code, this is the top-level-function of the masterVPs, and 21 *Within the code, this is the top-level-function of the masterVPs, and
22 * runs when the coreController has no more slave VPs. It's job is to 22 * runs when the coreController has no more slave VPs. It's job is to
23 * refill the animation slots with slaves. 23 * refill the animation slots with slaves that have work.
24 * 24 *
25 *To do this, it scans the animation slots for just-completed slaves. 25 *There are multiple versions of the master, each tuned to a specific
26 * Each of these has a request in it. So, the master hands each to the 26 * combination of modes. This keeps the master simple, with reduced overhead,
27 * plugin's request handler. 27 * when the application is not using the extra complexity.
28 *
29 *As of Sept 2012, the versions available will be:
30 * 1) Single langauge, which only exposes slaves (such as SSR or Vthread)
31 * 2) Single language, which only exposes tasks (such as pure dataflow)
32 * 3) Single language, which exposes both (like Cilk, StarSs, and OpenMP)
33 * 4) Multi-language, which always assumes both tasks and slaves
34 * 5) Multi-language and multi-process, which also assumes both tasks and slaves
35 *
36 *
37 *
38 */
39
40
41 //===================== The versions of the Animation Master =================
42 //
43 //==============================================================================
44
45 /* 1) This version is for a single language, that has only slaves, no tasks,
46 * such as Vthread or SSR.
47 *This version is for when an application has only a single language, and
48 * that language exposes slaves explicitly (as opposed to a task based
49 * language like pure dataflow).
50 *
51 *
52 *It scans the animation slots for just-completed slaves.
53 * Each completed slave has a request in it. So, the master hands each to
54 * the plugin's request handler (there is only one plugin, because only one
55 * lang).
28 *Each request represents a language construct that has been encountered 56 *Each request represents a language construct that has been encountered
29 * by the application code in the slave. Passing the request to the 57 * by the application code in the slave. Passing the request to the
30 * request handler is how that language construct's behavior gets invoked. 58 * request handler is how that language construct's behavior gets invoked.
31 * The request handler then performs the actions of the construct's 59 * The request handler then performs the actions of the construct's
32 * behavior. So, the request handler encodes the behavior of the 60 * behavior. So, the request handler encodes the behavior of the
75 *Implementation Details: 103 *Implementation Details:
76 * 104 *
77 *There is a separate masterVP for each core, but a single semantic 105 *There is a separate masterVP for each core, but a single semantic
78 * environment shared by all cores. Each core also has its own scheduling 106 * environment shared by all cores. Each core also has its own scheduling
79 * slots, which are used to communicate slaves between animationMaster and 107 * slots, which are used to communicate slaves between animationMaster and
80 * coreController. There is only one global variable, _VMSMasterEnv, which 108 * coreController. There is only one global variable, _PRMasterEnv, which
81 * holds the semantic env and other things shared by the different 109 * holds the semantic env and other things shared by the different
82 * masterVPs. The request handler and Assigner are registered with 110 * masterVPs. The request handler and Assigner are registered with
83 * the animationMaster by the language's init function, and a pointer to 111 * the animationMaster by the language's init function, and a pointer to
84 * each is in the _VMSMasterEnv. (There are also some pthread related global 112 * each is in the _PRMasterEnv. (There are also some pthread related global
85 * vars, but they're only used during init of VMS). 113 * vars, but they're only used during init of PR).
86 *VMS gains control over the cores by essentially "turning off" the OS's 114 *PR gains control over the cores by essentially "turning off" the OS's
87 * scheduler, using pthread pin-to-core commands. 115 * scheduler, using pthread pin-to-core commands.
88 * 116 *
89 *The masterVPs are created during init, with this animationMaster as their 117 *The masterVPs are created during init, with this animationMaster as their
90 * top level function. The masterVPs use the same SlaveVP data structure, 118 * top level function. The masterVPs use the same SlaveVP data structure,
91 * even though they're not slave VPs. 119 * even though they're not slave VPs.
92 *A "seed slave" is also created during init -- this is equivalent to the 120 *A "seed slave" is also created during init -- this is equivalent to the
93 * "main" function in C, and acts as the entry-point to the VMS-language- 121 * "main" function in C, and acts as the entry-point to the PR-language-
94 * based application. 122 * based application.
95 *The masterVPs shared a single system-wide master-lock, so only one 123 *The masterVPs share a single system-wide master-lock, so only one
96 * masterVP may be animated at a time. 124 * masterVP may be animated at a time.
97 *The core controllers access _VMSMasterEnv to get the masterVP, and when 125 *The core controllers access _PRMasterEnv to get the masterVP, and when
98 * they start, the slots are all empty, so they run their associated core's 126 * they start, the slots are all empty, so they run their associated core's
99 * masterVP. The first of those to get the master lock sees the seed slave 127 * masterVP. The first of those to get the master lock sees the seed slave
100 * in the shared semantic environment, so when it runs the Assigner, that 128 * in the shared semantic environment, so when it runs the Assigner, that
101 * returns the seed slave, which the animationMaster puts into a scheduling 129 * returns the seed slave, which the animationMaster puts into a scheduling
102 * slot then switches to the core controller. That then switches the core 130 * slot then switches to the core controller. That then switches the core
103 * over to the seed slave, which then proceeds to execute language 131 * over to the seed slave, which then proceeds to execute language
104 * constructs to create more slaves, and so on. Each of those constructs 132 * constructs to create more slaves, and so on. Each of those constructs
105 * causes the seed slave to suspend, switching over to the core controller, 133 * causes the seed slave to suspend, switching over to the core controller,
106 * which eventually switches to the masterVP, which executes the 134 * which eventually switches to the masterVP, which executes the
107 * request handler, which uses VMS primitives to carry out the creation of 135 * request handler, which uses PR primitives to carry out the creation of
108 * new slave VPs, which are marked as ready for the Assigner, and so on.. 136 * new slave VPs, which are marked as ready for the Assigner, and so on..
109 * 137 *
110 *On animation slots, and system behavior: 138 *On animation slots, and system behavior:
111 * A request may linger in a animation slot for a long time while 139 * A request may linger in an animation slot for a long time while
112 * the slaves in the other slots are animated. This only becomes a problem 140 * the slaves in the other slots are animated. This only becomes a problem
113 * when such a request is a choke-point in the constraints, and is needed 141 * when such a request is a choke-point in the constraints, and is needed
114 * to free work for *other* cores. To reduce this occurance, the number 142 * to free work for *other* cores. To reduce this occurrence, the number
115 * of animation slots should be kept low. In balance, having multiple 143 * of animation slots should be kept low. In balance, having multiple
116 * animation slots amortizes the overhead of switching to the masterVP and 144 * animation slots amortizes the overhead of switching to the masterVP and
117 * executing the animationMaster code, which drives for more than one. In 145 * executing the animationMaster code, which drives for more than one. In
118 * practice, the best balance should be discovered by profiling. 146 * practice, the best balance should be discovered by profiling.
119 */ 147 */
161 currSlot->needsSlaveAssigned = TRUE; 189 currSlot->needsSlaveAssigned = TRUE;
162 190
163 HOLISTIC__Record_AppResponder_start; 191 HOLISTIC__Record_AppResponder_start;
164 MEAS__startReqHdlr; 192 MEAS__startReqHdlr;
165 193
166 //process the requests made by the slave (held inside slave struc) 194 currSlot->workIsDone = FALSE;
195 currSlot->needsSlaveAssigned = TRUE;
196 SlaveVP *currSlave = currSlot->slaveAssignedToSlot;
197
198 justAddedReqHdlrChg();
199 //handle the request, either by VMS or by the language
200 if( currSlave->requests->reqType != LangReq )
201 { //The request is a standard VMS one, not one defined by the
202 // language, so VMS handles it, then queues slave to be assigned
203 handleReqInVMS( currSlave );
204 writePrivQ( currSlave, VMSReadyQ ); //Q slave to be assigned below
205 }
206 else
207 { MEAS__startReqHdlr;
208
209 //Language handles request, which is held inside slave struc
210 (*requestHandler)( currSlave, semanticEnv );
211
212 MEAS__endReqHdlr;
213 }
214 }
215
216 //process the requests made by the slave (held inside slave struc)
167 (*requestHandler)( currSlot->slaveAssignedToSlot, semanticEnv ); 217 (*requestHandler)( currSlot->slaveAssignedToSlot, semanticEnv );
168 218
169 HOLISTIC__Record_AppResponder_end; 219 HOLISTIC__Record_AppResponder_end;
170 MEAS__endReqHdlr; 220 MEAS__endReqHdlr;
171 } 221 }
194 flushRegisters(); 244 flushRegisters();
195 DEBUG__printf(FALSE,"came back after switch to core -- so lock released!"); 245 DEBUG__printf(FALSE,"came back after switch to core -- so lock released!");
196 }//while(1) 246 }//while(1)
197 } 247 }
198 248
249
250 /* 2) This version is for a single language that has only tasks, which
251 * cannot be suspended.
252 */
253 void animationMaster( void *initData, SlaveVP *masterVP )
254 {
255 //Used while scanning and filling animation slots
256 int32 slotIdx, numSlotsFilled;
257 AnimSlot *currSlot, **animSlots;
258 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
259
260 //Local copies, for performance
261 MasterEnv *masterEnv;
262 SlaveAssigner slaveAssigner;
263 RequestHandler requestHandler;
264 PRSemEnv *semanticEnv;
265 int32 thisCoresIdx;
266
267 //#ifdef MODE__MULTI_LANG
268 SlaveVP *slave;
269 PRProcess *process;
270 PRConstrEnvHolder *constrEnvHolder;
271 int32 langMagicNumber;
272 //#endif
273
274 //======================== Initializations ========================
275 masterEnv = (MasterEnv*)_PRMasterEnv;
276
277 thisCoresIdx = masterVP->coreAnimatedBy;
278 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
279
280 requestHandler = masterEnv->requestHandler;
281 slaveAssigner = masterEnv->slaveAssigner;
282 semanticEnv = masterEnv->semanticEnv;
283
284 //initialize, for non-multi-lang, non multi-proc case
285 // default handler gets put into master env by a registration call by lang
286 endTaskHandler = masterEnv->defaultTaskHandler;
287
288 HOLISTIC__Insert_Master_Global_Vars;
289
290 //======================== animationMaster ========================
291 //Do loop gets requests handled and work assigned to slots..
292 // work can either be a task or a resumed slave
293 //Having two cases makes this logic complex.. can be finishing either, and
294 // then the next available work may be either.. so really have two distinct
295 // loops that are inter-twined..
296 while(1){
297
298 MEAS__Capture_Pre_Master_Point
299
300 //Scan the animation slots
301 numSlotsFilled = 0;
302 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
303 {
304 currSlot = animSlots[ slotIdx ];
305
306 //Check if newly-done slave in slot, which will need request handled
307 if( currSlot->workIsDone )
308 { currSlot->workIsDone = FALSE;
309
310 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
311 MEAS__startReqHdlr;
312
313
314 //process the request made by the slave (held inside slave struc)
315 slave = currSlot->slaveAssignedToSlot;
316
317 //check if the completed work was a task..
318 if( slave->taskMetaInfo->isATask )
319 {
320 if( slave->reqst->type == TaskEnd )
321 { //do task end handler, which is registered separately
322 //note, end hdlr may use semantic data from reqst..
323 //#ifdef MODE__MULTI_LANG
324 //get end-task handler
325 //taskEndHandler = lookup( slave->reqst->langMagicNumber, processEnv );
326 taskEndHandler = slave->taskMetaInfo->endTaskHandler;
327 //#endif
328 (*taskEndHandler)( slave, semanticEnv );
329
330 goto AssignWork;
331 }
332 else //is a task, and just suspended
333 { //turn slot slave into free task slave & make replacement
334 if( slave->typeOfVP == TaskSlotSlv ) changeSlvType();
335
336 //goto normal slave request handling
337 goto SlaveReqHandling;
338 }
339 }
340 else //is a slave that suspended
341 {
342 SlaveReqHandling:
343 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
344
345 HOLISTIC__Record_AppResponder_end;
346 MEAS__endReqHdlr;
347
348 goto AssignWork;
349 }
350 } //if has suspended slave that needs handling
351
352 //if slot empty, hand to Assigner to fill with a slave
353 if( currSlot->needsSlaveAssigned )
354 { //Call plugin's Assigner to give slot a new slave
355 HOLISTIC__Record_Assigner_start;
356
357 AssignWork:
358
359 assignedSlaveVP = assignWork( semanticEnv, currSlot );
360
361 //put the chosen slave into slot, and adjust flags and state
362 if( assignedSlaveVP != NULL )
363 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
364 assignedSlaveVP->animSlotAssignedTo = currSlot;
365 currSlot->needsSlaveAssigned = FALSE;
366 numSlotsFilled += 1;
367 }
368 else
369 {
370 currSlot->needsSlaveAssigned = TRUE; //local write
371 }
372 HOLISTIC__Record_Assigner_end;
373 }//if slot needs slave assigned
374 }//for( slotIdx..
375
376 MEAS__Capture_Post_Master_Point;
377
378 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
379 flushRegisters();
380 }//while(1)
381 }
382
383
384 /*This is the master when just multi-lang, but not multi-process mode is on.
385 * This version has to handle both tasks and slaves, and do extra work of
386 * looking up the semantic env and handlers to use, for each completed bit of
387 * work.
388 *It also has to search through the semantic envs to find one with work,
389 * then ask that env's assigner to return a unit of that work.
390 *
391 *The language is written to startup in the same way as if it were the only
392 * language in the app, and it operates in the same way,
393 * the only difference between single language and multi-lang is here, in the
394 * master.
395 *This invisibility to mode is why the language has to use registration calls
396 * for everything during startup -- those calls do different things depending
397 * on whether it's single-language or multi-language mode.
398 *
399 *In this version of the master, work can either be a task or a resumed slave
400 *Having two cases makes this logic complex.. can be finishing either, and
401 * then the next available work may be either.. so really have two distinct
402 * loops that are inter-twined..
403 *
404 *Some special cases:
405 * A task-end is a special case for a few reasons (below).
406 * A task-end can't block a slave (can't cause it to "logically suspend")
407 * A task available for work can only be assigned to a special slave, which
408 * has been set aside for doing tasks, one such task-slave is always
409 * assigned to each slot. So, when a task ends, a new task is assigned to
410 * that slot's task-slave right away.
411 * But if no tasks are available, then have to switch over to looking at
412 * slaves to find one ready to resume, to find work for the slot.
413 * If a task just suspends, not ends, then its task-slave is no longer
414 * available to take new tasks, so a new task-slave has to be assigned to
415 * that slot. Then the slave of the suspended task is turned into a free
416 * task-slave and request handling is done on it as if it were a slave
417 * that suspended.
418 * After request handling, do the same sequence of looking for a task to be
419 * work, and if none, look for a slave ready to resume, as work for the slot.
420 * If a slave suspends, handle its request, then look for work.. first for a
421 * task to assign, and if none, slaves ready to resume.
422 * Another special case is when task-end is done on a free task-slave.. in
423 * that case, the slave has no more work and no way to get more.. so place
424 * it into a recycle queue.
425 * If no work is found of either type, then do a special thing to prune down
426 * the extra slaves in the recycle queue, just so don't get too many..
427 *
428 *The multi-lang thing complicates matters..
429 *
430 *For request handling, it means have to first fetch the semantic environment
431 * of the language, and then do the request handler pointed to by that
432 * semantic env.
433 *For assigning, things get more complex because of competing goals.. One
434 * goal is for language specific stuff to be used during assignment, so
435 * assigner can make higher quality decisions.. but with multiple languages,
436 * which only get mixed in the application, the assigners can't be written
437 * with knowledge of each other. So, they can only make localized decisions,
438 * and so different language's assigners may interfere with each other..
439 *
440 *So, have some possibilities available:
441 *1) can have a fixed scheduler in the proto-runtime, that all the
442 * languages give their work to.. (but then lose language-specific info,
443 * there is a standard PR format for assignment info, and the langauge
444 * attaches this to the work-unit when it gives it to PR.. also have issue
445 * with HWSim, which uses a priority Q instead of FIFO, and requests can
446 * "undo" previous work put in, so request handlers need way to manipulate
447 * the work-holding Q..) (this might be fudgeable with
448 * HWSim, if the master did a lang-supplied callback each time it assigns a
449 * unit to a slot.. then HWSim can keep exactly one unit of work in PR's
450 * queue at a time.. but this is quite hack-like.. or perhaps HWSim supplies
451 * a task-end handler that kicks the next unit of work from HWSim internal
452 * priority queue, over to PR readyQ)
453 *2) can have each language have its own semantic env, that holds its own
454 * work, which is assigned by its own assigner.. then the master searches
455 * through all the semantic envs to find one with work and asks it give work..
456 * (this has downside of blinding assigners to each other.. but does work
457 * for HWSim case)
458 *3) could make PR have a different readyQ for each core, and ask the lang
459 * to put work to the core it prefers.. but the work may be moved by PR if
460 * needed, say if one core idles for too long. This is a hybrid approach,
461 * letting the language decide which core, but PR keeps the work and does it
462 * FIFO style.. (this might als be fudgeable with HWSim, in similar fashion,
463 * but it would be complicated by having to track cores separately)
464 *
465 *Choosing 2, to keep compatibility with single-lang mode.. it allows the same
466 * assigner to be used for single-lang as for multi-lang.. the overhead of
467 * the extra master search for work is part of the price of the flexibility,
468 * but should be fairly small.. takes the first env that has work available,
469 * and whatever it returns is assigned to the slot..
470 *
471 *As a hybrid, giving an option for a unified override assigner to be registered
472 * and used.. This allows something like a static analysis to detect
473 * which languages are grouped together, and then analyze the pattern of
474 * construct calls, and generate a custom assigner that uses info from all
475 * the languages in a unified way.. Don't really expect this to happen,
476 * but making it possible.
477 */
478 #ifdef MODE__MULTI_LANG
479 void animationMaster( void *initData, SlaveVP *masterVP )
480 {
481 //Used while scanning and filling animation slots
482 int32 slotIdx, numSlotsFilled;
483 AnimSlot *currSlot, **animSlots;
484 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
485
486 //Local copies, for performance
487 MasterEnv *masterEnv;
488 SlaveAssigner slaveAssigner;
489 RequestHandler requestHandler;
490 PRSemEnv *semanticEnv;
491 int32 thisCoresIdx;
492
493 //#ifdef MODE__MULTI_LANG
494 SlaveVP *slave;
495 PRProcess *process;
496 PRConstrEnvHolder *constrEnvHolder;
497 int32 langMagicNumber;
498 //#endif
499
500 //======================== Initializations ========================
501 masterEnv = (MasterEnv*)_PRMasterEnv;
502
503 thisCoresIdx = masterVP->coreAnimatedBy;
504 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
505
506 requestHandler = masterEnv->requestHandler;
507 slaveAssigner = masterEnv->slaveAssigner;
508 semanticEnv = masterEnv->semanticEnv;
509
510 //initialize, for non-multi-lang, non multi-proc case
511 // default handler gets put into master env by a registration call by lang
512 endTaskHandler = masterEnv->defaultTaskHandler;
513
514 HOLISTIC__Insert_Master_Global_Vars;
515
516 //======================== animationMaster ========================
517 //Do loop gets requests handled and work assigned to slots..
518 // work can either be a task or a resumed slave
519 //Having two cases makes this logic complex.. can be finishing either, and
520 // then the next available work may be either.. so really have two distinct
521 // loops that are inter-twined..
522 while(1){
523
524 MEAS__Capture_Pre_Master_Point
525
526 //Scan the animation slots
527 numSlotsFilled = 0;
528 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
529 {
530 currSlot = animSlots[ slotIdx ];
531
532 //Check if newly-done slave in slot, which will need request handled
533 if( currSlot->workIsDone )
534 { currSlot->workIsDone = FALSE;
535
536 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
537 MEAS__startReqHdlr;
538
539
540 //process the request made by the slave (held inside slave struc)
541 slave = currSlot->slaveAssignedToSlot;
542
543 //check if the completed work was a task..
544 if( slave->taskMetaInfo->isATask )
545 {
546 if( slave->reqst->type == TaskEnd )
547 { //do task end handler, which is registered separately
548 //note, end hdlr may use semantic data from reqst..
549 //#ifdef MODE__MULTI_LANG
550 //get end-task handler
551 //taskEndHandler = lookup( slave->reqst->langMagicNumber, processEnv );
552 taskEndHandler = slave->taskMetaInfo->endTaskHandler;
553 //#endif
554 (*taskEndHandler)( slave, semanticEnv );
555
556 goto AssignWork;
557 }
558 else //is a task, and just suspended
559 { //turn slot slave into free task slave & make replacement
560 if( slave->typeOfVP == TaskSlotSlv ) changeSlvType();
561
562 //goto normal slave request handling
563 goto SlaveReqHandling;
564 }
565 }
566 else //is a slave that suspended
567 {
568 SlaveReqHandling:
569 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
570
571 HOLISTIC__Record_AppResponder_end;
572 MEAS__endReqHdlr;
573
574 goto AssignWork;
575 }
576 } //if has suspended slave that needs handling
577
578 //if slot empty, hand to Assigner to fill with a slave
579 if( currSlot->needsSlaveAssigned )
580 { //Call plugin's Assigner to give slot a new slave
581 HOLISTIC__Record_Assigner_start;
582
583 AssignWork:
584
585 assignedSlaveVP = assignWork( semanticEnv, currSlot );
586
587 //put the chosen slave into slot, and adjust flags and state
588 if( assignedSlaveVP != NULL )
589 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
590 assignedSlaveVP->animSlotAssignedTo = currSlot;
591 currSlot->needsSlaveAssigned = FALSE;
592 numSlotsFilled += 1;
593 }
594 else
595 {
596 currSlot->needsSlaveAssigned = TRUE; //local write
597 }
598 HOLISTIC__Record_Assigner_end;
599 }//if slot needs slave assigned
600 }//for( slotIdx..
601
602 MEAS__Capture_Post_Master_Point;
603
604 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
605 flushRegisters();
606 }//while(1)
607 }
608 #endif //MODE__MULTI_LANG
609
610
611
612 //This is the master when both multi-lang and multi-process modes are turned on
613 //#ifdef MODE__MULTI_LANG
614 //#ifdef MODE__MULTI_PROCESS
615 void animationMaster( void *initData, SlaveVP *masterVP )
616 {
617 //Used while scanning and filling animation slots
618 int32 slotIdx, numSlotsFilled;
619 AnimSlot *currSlot, **animSlots;
620 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
621
622 //Local copies, for performance
623 MasterEnv *masterEnv;
624 SlaveAssigner slaveAssigner;
625 RequestHandler requestHandler;
626 PRSemEnv *semanticEnv;
627 int32 thisCoresIdx;
628
629 SlaveVP *slave;
630 PRProcess *process;
631 PRConstrEnvHolder *constrEnvHolder;
632 int32 langMagicNumber;
633
634 //======================== Initializations ========================
635 masterEnv = (MasterEnv*)_PRMasterEnv;
636
637 thisCoresIdx = masterVP->coreAnimatedBy;
638 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
639
640 requestHandler = masterEnv->requestHandler;
641 slaveAssigner = masterEnv->slaveAssigner;
642 semanticEnv = masterEnv->semanticEnv;
643
644 //initialize, for non-multi-lang, non multi-proc case
645 // default handler gets put into master env by a registration call by lang
646 endTaskHandler = masterEnv->defaultTaskHandler;
647
648 HOLISTIC__Insert_Master_Global_Vars;
649
650 //======================== animationMaster ========================
651 //Do loop gets requests handled and work assigned to slots..
652 // work can either be a task or a resumed slave
653 //Having two cases makes this logic complex.. can be finishing either, and
654 // then the next available work may be either.. so really have two distinct
655 // loops that are inter-twined..
656 while(1){
657
658 MEAS__Capture_Pre_Master_Point
659
660 //Scan the animation slots
661 numSlotsFilled = 0;
662 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
663 {
664 currSlot = animSlots[ slotIdx ];
665
666 //Check if newly-done slave in slot, which will need request handled
667 if( currSlot->workIsDone )
668 { currSlot->workIsDone = FALSE;
669
670 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
671 MEAS__startReqHdlr;
672
673
674 //process the request made by the slave (held inside slave struc)
675 slave = currSlot->slaveAssignedToSlot;
676
677 //check if the completed work was a task..
678 if( slave->taskMetaInfo->isATask )
679 {
680 if( slave->reqst->type == TaskEnd )
681 { //do task end handler, which is registered separately
682 //note, end hdlr may use semantic data from reqst..
683 //get end-task handler
684 //taskEndHandler = lookup( slave->reqst->langMagicNumber, processEnv );
685 taskEndHandler = slave->taskMetaInfo->endTaskHandler;
686
687 (*taskEndHandler)( slave, semanticEnv );
688
689 goto AssignWork;
690 }
691 else //is a task, and just suspended
692 { //turn slot slave into free task slave & make replacement
693 if( slave->typeOfVP == TaskSlotSlv ) changeSlvType();
694
695 //goto normal slave request handling
696 goto SlaveReqHandling;
697 }
698 }
699 else //is a slave that suspended
700 {
701
702 SlaveReqHandling:
703 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
704
705 HOLISTIC__Record_AppResponder_end;
706 MEAS__endReqHdlr;
707
708 goto AssignWork;
709 }
710 } //if has suspended slave that needs handling
711
712 //if slot empty, hand to Assigner to fill with a slave
713 if( currSlot->needsSlaveAssigned )
714 { //Scan sem environs, looking for one with ready work.
715 // call the Assigner for that sem Env, to give slot a new slave
716 HOLISTIC__Record_Assigner_start;
717
718 AssignWork:
719
720 assignedSlaveVP = assignWork( semanticEnv, currSlot );
721
722 //put the chosen slave into slot, and adjust flags and state
723 if( assignedSlaveVP != NULL )
724 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
725 assignedSlaveVP->animSlotAssignedTo = currSlot;
726 currSlot->needsSlaveAssigned = FALSE;
727 numSlotsFilled += 1;
728 }
729 else
730 {
731 currSlot->needsSlaveAssigned = TRUE; //local write
732 }
733 HOLISTIC__Record_Assigner_end;
734 }//if slot needs slave assigned
735 }//for( slotIdx..
736
737 MEAS__Capture_Post_Master_Point;
738
739 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
740 flushRegisters();
741 }//while(1)
742 }
743 #endif //MODE__MULTI_LANG
744 #endif //MODE__MULTI_PROCESS
745
746
747 /*This does three things:
748 * 1) ask for a slave ready to resume
749 * 2) if none, then ask for a task, and assign to the slot slave
750 * 3) if none, then prune former task slaves waiting to be recycled.
751 *
752 //Have two separate assigners in each semantic env,
753 // which keeps its own work in its own structures.. the master, here,
754 // searches through the semantic environs, takes the first that has work
755 // available, and whatever it returns is assigned to the slot..
756 //However, also have an override assigner.. because static analysis tools know
757 // which languages are grouped together.. and the override enables them to
758 // generate a custom assigner that uses info from all the languages in a
759 // unified way.. Don't really expect this to happen, but making it possible.
760 */
761 inline SlaveVP *
762 assignWork( PRProcessEnv *processEnv, AnimSlot *slot )
763 { SlaveVP *returnSlv;
764 //VSsSemEnv *semEnv;
765 //VSsSemData *semData;
766 int32 coreNum, slotNum;
767 PRTaskMetaInfo *newTaskStub;
768 SlaveVP *freeTaskSlv;
769
770
771 //master has to handle slot slaves.. so either assigner returns
772 // taskMetaInfo or else two assigners, one for slaves, other for tasks..
773 semEnvs = processEnv->semEnvs;
774 numEnvs = processEnv->numSemEnvs;
775 for( envIdx = 0; envIdx < numEnvs; envIdx++ )
776 { semEnv = semEnvs[envIdx];
777 if( semEnv->hasWork )
778 { assigner = semEnv->assigner;
779 retTaskMetaInfo = (*assigner)( semEnv, slot );
780
781 return retTaskMetaInfo; //quit, have work
782 }
783 }
784
785 coreNum = slot->coreSlotIsOn;
786 slotNum = slot->slotIdx;
787
788 //first try to get a ready slave
789 returnSlv = getReadySlave();
790
791 if( returnSlv != NULL )
792 { returnSlv->coreAnimatedBy = coreNum;
793
794 //have work, so reset Done flag (when work generated on other core)
795 if( processEnv->coreIsDone[coreNum] == TRUE ) //reads are higher perf
796 processEnv->coreIsDone[coreNum] = FALSE; //don't just write always
797
798 goto ReturnTheSlv;
799 }
800
801 //were no slaves, so try to get a ready task..
802 newTaskStub = getTaskStub();
803
804 if( newTaskStub != NULL )
805 {
806 //get the slot slave to assign the task to..
807 returnSlv = processEnv->slotTaskSlvs[coreNum][slotNum];
808
809 //point slave to task's function, and mark slave as having task
810 PR_int__reset_slaveVP_to_TopLvlFn( returnSlv,
811 newTaskStub->taskType->fn, newTaskStub->args );
812 returnSlv->taskStub = newTaskStub;
813 newTaskStub->slaveAssignedTo = returnSlv;
814 returnSlv->needsTaskAssigned = FALSE; //slot slave is a "Task" slave type
815
816 //have work, so reset Done flag, if was set
817 if( processEnv->coreIsDone[coreNum] == TRUE ) //reads are higher perf
818 processEnv->coreIsDone[coreNum] = FALSE; //don't just write always
819
820 goto ReturnTheSlv;
821 }
822 else
823 { //no task, so prune the recycle pool of free task slaves
824 freeTaskSlv = readPrivQ( processEnv->freeTaskSlvRecycleQ );
825 if( freeTaskSlv != NULL )
826 { //delete to bound the num extras, and deliver shutdown cond
827 handleDissipate( freeTaskSlv, processEnv );
828 //then return NULL
829 returnSlv = NULL;
830
831 goto ReturnTheSlv;
832 }
833 else
834 { //candidate for shutdown.. if all extras dissipated, and no tasks
835 // and no ready to resume slaves, then no way to generate
836 // more tasks (on this core -- other core might have task still)
837 if( processEnv->numLiveExtraTaskSlvs == 0 &&
838 processEnv->numLiveThreadSlvs == 0 )
839 { //This core sees no way to generate more tasks, so say it
840 if( processEnv->coreIsDone[coreNum] == FALSE )
841 { processEnv->numCoresDone += 1;
842 processEnv->coreIsDone[coreNum] = TRUE;
843 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
844 processEnv->shutdownInitiated = TRUE;
845
846 #else
847 if( processEnv->numCoresDone == NUM_CORES )
848 { //means no cores have work, and none can generate more
849 processEnv->shutdownInitiated = TRUE;
850 }
851 #endif
852 }
853 }
854 //check if shutdown has been initiated by this or other core
855 if(processEnv->shutdownInitiated)
856 { returnSlv = PR_SS__create_shutdown_slave();
857 }
858 else
859 returnSlv = NULL;
860
861 goto ReturnTheSlv; //don't need, but completes pattern
862 } //if( freeTaskSlv != NULL )
863 } //if( newTaskStub == NULL )
864 //outcome: 1)slave was just pointed to task, 2)no tasks, so slave NULL
865
866
867 ReturnTheSlv: //All paths goto here.. to provide single point for holistic..
868
869 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
870 if( returnSlv == NULL )
871 { returnSlv = processEnv->idleSlv[coreNum][slotNum];
872
873 //things that would normally happen in resume(), but idle VPs
874 // never go there
875 returnSlv->assignCount++; //gives each idle unit a unique ID
876 Unit newU;
877 newU.vp = returnSlv->slaveID;
878 newU.task = returnSlv->assignCount;
879 addToListOfArrays(Unit,newU,processEnv->unitList);
880
881 if (returnSlv->assignCount > 1) //make a dependency from prev idle unit
882 { Dependency newD; // to this one
883 newD.from_vp = returnSlv->slaveID;
884 newD.from_task = returnSlv->assignCount - 1;
885 newD.to_vp = returnSlv->slaveID;
886 newD.to_task = returnSlv->assignCount;
887 addToListOfArrays(Dependency, newD ,processEnv->ctlDependenciesList);
888 }
889 }
890 else //have a slave will be assigned to the slot
891 { //assignSlv->numTimesAssigned++;
892 //get previous occupant of the slot
893 Unit prev_in_slot =
894 processEnv->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum];
895 if(prev_in_slot.vp != 0) //if not first slave in slot, make dependency
896 { Dependency newD; // is a hardware dependency
897 newD.from_vp = prev_in_slot.vp;
898 newD.from_task = prev_in_slot.task;
899 newD.to_vp = returnSlv->slaveID;
900 newD.to_task = returnSlv->assignCount;
901 addToListOfArrays(Dependency,newD,processEnv->hwArcs);
902 }
903 prev_in_slot.vp = returnSlv->slaveID; //make new slave the new previous
904 prev_in_slot.task = returnSlv->assignCount;
905 processEnv->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum] =
906 prev_in_slot;
907 }
908 #endif
909
910 return( returnSlv );
911 }
912
913
914 //=================================================================
915 //#else //is MODE__MULTI_LANG
916 //For multi-lang mode, first, get the constraint-env holder out of
917 // the process, which is in the slave.
918 //Second, get the magic number out of the request, use it to look up
919 // the constraint Env within the constraint-env holder.
920 //Then get the request handler out of the constr env
921 constrEnvHolder = slave->process->constrEnvHolder;
922 reqst = slave->request;
923 langMagicNumber = reqst->langMagicNumber;
924 semanticEnv = lookup( langMagicNumber, constrEnvHolder ); //a macro
925 if( slave->reqst->type == taskEnd ) //end-task is special
926 { //need to know what lang's task ended
927 taskEndHandler = semanticEnv->taskEndHandler;
928 (*taskEndHandler)( slave, reqst, semanticEnv ); //can put semantic data into task end reqst, for continuation, etc
929 //this is a slot slave, get a new task for it
930 if( !existsOverrideAssigner )//if exists, is set above, before loop
931 { //search for task assigner that has work
932 for( a = 0; a < num_assigners; a++ )
933 { if( taskAssigners[a]->hasWork )
934 { newTaskAssigner = taskAssigners[a];
935 (*newTaskAssigner)( slave, semanticEnv );
936 goto GotTask;
937 }
938 }
939 goto NoTasks;
940 }
941
942 GotTask:
943 continue; //have work, so do next iter of loop, don't call slave assigner
944 }
945 if( slave->typeOfVP == taskSlotSlv ) changeSlvType();//is suspended task
946 //now do normal suspended slave request handler
947 requestHandler = semanticEnv->requestHandler;
948 //#endif
949
950
951 }
952 //If make it here, then was no task for this slot
953 //slot empty, hand to Assigner to fill with a slave
954 if( currSlot->needsSlaveAssigned )
955 { //Call plugin's Assigner to give slot a new slave
956 HOLISTIC__Record_Assigner_start;
957
958 //#ifdef MODE__MULTI_LANG
959 NoTasks:
960 //First, choose an Assigner..
961 //There are several Assigners, one for each langlet.. they all
962 // indicate whether they have work available.. just pick the first
963 // one that has work.. Or, if there's a Unified Assigner, call
964 // that one.. So, go down array, checking..
965 if( !existsOverrideAssigner )
966 { for( a = 0; a < num_assigners; a++ )
967 { if( assigners[a]->hasWork )
968 { slaveAssigner = assigners[a];
969 goto GotAssigner;
970 }
971 }
972 //no work, so just continue to next iter of scan loop
973 continue;
974 }
975 //when exists override, the assigner is set, once, above, so do nothing
976 GotAssigner:
977 //#endif
978
979 assignedSlaveVP =
980 (*slaveAssigner)( semanticEnv, currSlot );
981
982 //put the chosen slave into slot, and adjust flags and state
983 if( assignedSlaveVP != NULL )
984 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
985 assignedSlaveVP->animSlotAssignedTo = currSlot;
986 currSlot->needsSlaveAssigned = FALSE;
987 numSlotsFilled += 1;
988
989 HOLISTIC__Record_Assigner_end;
990 }
991 }//if slot needs slave assigned
992 }//for( slotIdx..
993
994 MEAS__Capture_Post_Master_Point;
995
996 masterSwitchToCoreCtlr( masterVP );
997 flushRegisters();
998 DEBUG__printf(FALSE,"came back after switch to core -- so lock released!");
999 }//while(1)
1000 }
1001