view AnimationMaster.c @ 261:dafae55597ce

Getting closer -- added PRServ as built-in langlet (but still just copy) about to rework a lot of the Master code.. possibly eliminate core controller
author Sean Halle <seanhalle@yahoo.com>
date Tue, 23 Oct 2012 23:46:17 -0700
parents 999f2966a3e5
children a5fa1e087c7e
line source
1 /*
2 * Copyright 2010 OpenSourceStewardshipFoundation
3 *
4 * Licensed under BSD
5 */
9 #include <stdio.h>
10 #include <stddef.h>
12 #include "PR.h"
13 #include "VSs_impl/VSs.h"
15 inline void
16 replaceWithNewSlotSlv( SlaveVP *requestingSlv, PRProcessEnv *processEnv );
19 /*The animationMaster embodies most of the animator of the language. The
20 * animator is what emodies the behavior of language constructs.
21 * As such, it is the animationMaster, in combination with the plugin
22 * functions, that make the language constructs do their behavior.
23 *
24 *Within the code, this is the top-level-function of the masterVPs, and
25 * runs when the coreController has no more slave VPs. It's job is to
26 * refill the animation slots with slaves that have work.
27 *
28 *There are multiple versions of the master, each tuned to a specific
29 * combination of modes. This keeps the master simple, with reduced overhead,
30 * when the application is not using the extra complexity.
31 *
32 *As of Sept 2012, the versions available will be:
33 * 1) Single langauge, which only exposes slaves (such as SSR or Vthread)
34 * 2) Single language, which only exposes tasks (such as pure dataflow)
35 * 3) Single language, which exposes both (like Cilk, StarSs, and OpenMP)
36 * 4) Multi-language, which always assumes both tasks and slaves
37 * 5) Multi-language and multi-process, which also assumes both tasks and slaves
38 *
39 *
40 *
41 */
44 //===================== The versions of the Animation Master =================
45 //
46 //==============================================================================
48 /* 1) This version is for a single language, that has only slaves, no tasks,
49 * such as Vthread or SSR.
50 *This version is for when an application has only a single language, and
51 * that language exposes slaves explicitly (as opposed to a task based
52 * language like pure dataflow).
53 *
54 *
55 *It scans the animation slots for just-completed slaves.
56 * Each completed slave has a request in it. So, the master hands each to
57 * the plugin's request handler (there is only one plugin, because only one
58 * lang).
59 *Each request represents a language construct that has been encountered
60 * by the application code in the slave. Passing the request to the
61 * request handler is how that language construct's behavior gets invoked.
62 * The request handler then performs the actions of the construct's
63 * behavior. So, the request handler encodes the behavior of the
64 * language's parallelism constructs, and performs that when the master
65 * hands it a slave containing a request to perform that construct.
66 *
67 *On a shared-memory machine, the behavior of parallelism constructs
68 * equals control, over order of execution of code. Hence, the behavior
69 * of the language constructs performed by the request handler is to
70 * choose the order that slaves get animated, and thereby control the
71 * order that application code in the slaves executes.
72 *
73 *To control order of animation of slaves, the request handler has a
74 * semantic environment that holds data structures used to hold slaves
75 * and choose when they're ready to be animated.
76 *
77 *Once a slave is marked as ready to be animated by the request handler,
78 * it is the second plugin function, the Assigner, which chooses the core
79 * the slave gets assigned to for animation. Hence, the Assigner doesn't
80 * perform any of the semantic behavior of language constructs, rather
81 * it gives the language a chance to improve performance. The performance
82 * of application code is strongly related to communication between
83 * cores. On shared-memory machines, communication is caused during
84 * execution of code, by memory accesses, and how much depends on contents
85 * of caches connected to the core executing the code. So, the placement
86 * of slaves determines the communication caused during execution of the
87 * slave's code.
88 *The point of the Assigner, then, is to use application information during
89 * execution of the program, to make choices about slave placement onto
90 * cores, with the aim to put slaves close to caches containing the data
91 * used by the slave's code.
92 *
93 *==========================================================================
94 *In summary, the animationMaster scans the slots, finds slaves
95 * just-finished, which hold requests, pass those to the request handler,
96 * along with the semantic environment, and the request handler then manages
97 * the structures in the semantic env, which controls the order of
98 * animation of slaves, and so embodies the behavior of the language
99 * constructs.
100 *The animationMaster then rescans the slots, offering each empty one to
101 * the Assigner, along with the semantic environment. The Assigner chooses
102 * among the ready slaves in the semantic Env, finding the one best suited
103 * to be animated by that slot's associated core.
104 *
105 *==========================================================================
106 *Implementation Details:
107 *
108 *There is a separate masterVP for each core, but a single semantic
109 * environment shared by all cores. Each core also has its own scheduling
110 * slots, which are used to communicate slaves between animationMaster and
111 * coreController. There is only one global variable, _PRTopEnv, which
112 * holds the semantic env and other things shared by the different
113 * masterVPs. The request handler and Assigner are registered with
114 * the animationMaster by the language's init function, and a pointer to
115 * each is in the _PRTopEnv. (There are also some pthread related global
116 * vars, but they're only used during init of PR).
117 *PR gains control over the cores by essentially "turning off" the OS's
118 * scheduler, using pthread pin-to-core commands.
119 *
120 *The masterVPs are created during init, with this animationMaster as their
121 * top level function. The masterVPs use the same SlaveVP data structure,
122 * even though they're not slave VPs.
123 *A "seed slave" is also created during init -- this is equivalent to the
124 * "main" function in C, and acts as the entry-point to the PR-language-
125 * based application.
126 *The masterVPs share a single system-wide master-lock, so only one
127 * masterVP may be animated at a time.
128 *The core controllers access _PRTopEnv to get the masterVP, and when
129 * they start, the slots are all empty, so they run their associated core's
130 * masterVP. The first of those to get the master lock sees the seed slave
131 * in the shared semantic environment, so when it runs the Assigner, that
132 * returns the seed slave, which the animationMaster puts into a scheduling
133 * slot then switches to the core controller. That then switches the core
134 * over to the seed slave, which then proceeds to execute language
135 * constructs to create more slaves, and so on. Each of those constructs
136 * causes the seed slave to suspend, switching over to the core controller,
137 * which eventually switches to the masterVP, which executes the
138 * request handler, which uses PR primitives to carry out the creation of
139 * new slave VPs, which are marked as ready for the Assigner, and so on..
140 *
141 *On animation slots, and system behavior:
142 * A request may linger in an animation slot for a long time while
143 * the slaves in the other slots are animated. This only becomes a problem
144 * when such a request is a choke-point in the constraints, and is needed
145 * to free work for *other* cores. To reduce this occurrence, the number
146 * of animation slots should be kept low. In balance, having multiple
147 * animation slots amortizes the overhead of switching to the masterVP and
148 * executing the animationMaster code, which drives for more than one. In
149 * practice, the best balance should be discovered by profiling.
150 */
151 void animationMaster( void *initData, SlaveVP *masterVP )
152 {
153 //Used while scanning and filling animation slots
154 int32 slotIdx, numSlotsFilled;
155 AnimSlot *currSlot, **animSlots;
156 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
158 //Local copies, for performance
159 MasterEnv *masterEnv;
160 SlaveAssigner slaveAssigner;
161 RequestHandler requestHandler;
162 void *semanticEnv;
163 int32 thisCoresIdx;
165 //======================== Initializations ========================
166 masterEnv = (MasterEnv*)_PRTopEnv;
168 thisCoresIdx = masterVP->coreAnimatedBy;
169 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
171 requestHandler = masterEnv->requestHandler;
172 slaveAssigner = masterEnv->slaveAssigner;
173 semanticEnv = masterEnv->semanticEnv;
175 HOLISTIC__Insert_Master_Global_Vars;
177 //======================== animationMaster ========================
178 while(1){
180 MEAS__Capture_Pre_Master_Point
182 //Scan the animation slots
183 numSlotsFilled = 0;
184 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
185 {
186 currSlot = animSlots[ slotIdx ];
188 //Check if newly-done slave in slot, which will need request handled
189 if( currSlot->workIsDone )
190 {
191 currSlot->workIsDone = FALSE;
192 currSlot->needsSlaveAssigned = TRUE;
194 HOLISTIC__Record_AppResponder_start;
195 MEAS__startReqHdlr;
197 currSlot->workIsDone = FALSE;
198 currSlot->needsSlaveAssigned = TRUE;
199 SlaveVP *currSlave = currSlot->slaveAssignedToSlot;
201 justAddedReqHdlrChg();
202 //handle the request, either by PR or by the language
203 if( currSlave->requests->reqType != LangReq )
204 { //The request is a standard PR one, not one defined by the
205 // language, so PR handles it, then queues slave to be assigned
206 handleReqInPR( currSlave );
207 writePrivQ( currSlave, PRReadyQ ); //Q slave to be assigned below
208 }
209 else
210 { MEAS__startReqHdlr;
212 //Language handles request, which is held inside slave struc
213 (*requestHandler)( currSlave, semanticEnv );
215 MEAS__endReqHdlr;
216 }
217 }
219 //process the requests made by the slave (held inside slave struc)
220 (*requestHandler)( currSlot->slaveAssignedToSlot, semanticEnv );
222 HOLISTIC__Record_AppResponder_end;
223 MEAS__endReqHdlr;
224 }
225 //If slot empty, hand to Assigner to fill with a slave
226 if( currSlot->needsSlaveAssigned )
227 { //Call plugin's Assigner to give slot a new slave
228 HOLISTIC__Record_Assigner_start;
229 assignedSlaveVP =
230 (*slaveAssigner)( semanticEnv, currSlot );
232 //put the chosen slave into slot, and adjust flags and state
233 if( assignedSlaveVP != NULL )
234 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
235 assignedSlaveVP->animSlotAssignedTo = currSlot;
236 currSlot->needsSlaveAssigned = FALSE;
237 numSlotsFilled += 1;
239 HOLISTIC__Record_Assigner_end;
240 }
241 }
242 }
244 MEAS__Capture_Post_Master_Point;
246 masterSwitchToCoreCtlr( masterVP );
247 flushRegisters();
248 DEBUG__printf(FALSE,"came back after switch to core -- so lock released!");
249 }//while(1)
250 }
253 /* 2) This version is for a single language that has only tasks, which
254 * cannot be suspended.
255 */
256 void animationMaster( void *initData, SlaveVP *masterVP )
257 {
258 //Used while scanning and filling animation slots
259 int32 slotIdx, numSlotsFilled;
260 AnimSlot *currSlot, **animSlots;
261 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
263 //Local copies, for performance
264 MasterEnv *masterEnv;
265 SlaveAssigner slaveAssigner;
266 RequestHandler requestHandler;
267 PRSemEnv *semanticEnv;
268 int32 thisCoresIdx;
270 //#ifdef MODE__MULTI_LANG
271 SlaveVP *slave;
272 PRProcess *process;
273 PRConstrEnvHolder *constrEnvHolder;
274 int32 langMagicNumber;
275 //#endif
277 //======================== Initializations ========================
278 masterEnv = (MasterEnv*)_PRTopEnv;
280 thisCoresIdx = masterVP->coreAnimatedBy;
281 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
283 requestHandler = masterEnv->requestHandler;
284 slaveAssigner = masterEnv->slaveAssigner;
285 semanticEnv = masterEnv->semanticEnv;
287 //initialize, for non-multi-lang, non multi-proc case
288 // default handler gets put into master env by a registration call by lang
289 endTaskHandler = masterEnv->defaultTaskHandler;
291 HOLISTIC__Insert_Master_Global_Vars;
293 //======================== animationMaster ========================
294 //Do loop gets requests handled and work assigned to slots..
295 // work can either be a task or a resumed slave
296 //Having two cases makes this logic complex.. can be finishing either, and
297 // then the next available work may be either.. so really have two distinct
298 // loops that are inter-twined..
299 while(1){
301 MEAS__Capture_Pre_Master_Point
303 //Scan the animation slots
304 numSlotsFilled = 0;
305 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
306 {
307 currSlot = animSlots[ slotIdx ];
309 //Check if newly-done slave in slot, which will need request handled
310 if( currSlot->workIsDone )
311 { currSlot->workIsDone = FALSE;
313 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
314 MEAS__startReqHdlr;
317 //process the request made by the slave (held inside slave struc)
318 slave = currSlot->slaveAssignedToSlot;
320 //check if the completed work was a task..
321 if( slave->taskMetaInfo->isATask )
322 {
323 if( slave->reqst->type == TaskEnd )
324 { //do task end handler, which is registered separately
325 //note, end hdlr may use semantic data from reqst..
326 //#ifdef MODE__MULTI_LANG
327 //get end-task handler
328 //taskEndHandler = lookup( slave->reqst->langMagicNumber, processEnv );
329 taskEndHandler = slave->taskMetaInfo->endTaskHandler;
330 //#endif
331 (*taskEndHandler)( slave, semanticEnv );
333 goto AssignWork;
334 }
335 else //is a task, and just suspended
336 { //turn slot slave into free task slave & make replacement
337 if( slave->typeOfVP == TaskSlotSlv ) changeSlvType();
339 //goto normal slave request handling
340 goto SlaveReqHandling;
341 }
342 }
343 else //is a slave that suspended
344 {
345 SlaveReqHandling:
346 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
348 HOLISTIC__Record_AppResponder_end;
349 MEAS__endReqHdlr;
351 goto AssignWork;
352 }
353 } //if has suspended slave that needs handling
355 //if slot empty, hand to Assigner to fill with a slave
356 if( currSlot->needsSlaveAssigned )
357 { //Call plugin's Assigner to give slot a new slave
358 HOLISTIC__Record_Assigner_start;
360 AssignWork:
362 assignedSlaveVP = assignWork( semanticEnv, currSlot );
364 //put the chosen slave into slot, and adjust flags and state
365 if( assignedSlaveVP != NULL )
366 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
367 assignedSlaveVP->animSlotAssignedTo = currSlot;
368 currSlot->needsSlaveAssigned = FALSE;
369 numSlotsFilled += 1;
370 }
371 else
372 {
373 currSlot->needsSlaveAssigned = TRUE; //local write
374 }
375 HOLISTIC__Record_Assigner_end;
376 }//if slot needs slave assigned
377 }//for( slotIdx..
379 MEAS__Capture_Post_Master_Point;
381 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
382 flushRegisters();
383 }//while(1)
384 }
387 /*This is the master when just multi-lang, but not multi-process mode is on.
388 * This version has to handle both tasks and slaves, and do extra work of
389 * looking up the semantic env and handlers to use, for each completed bit of
390 * work.
391 *It also has to search through the semantic envs to find one with work,
392 * then ask that env's assigner to return a unit of that work.
393 *
394 *The language is written to startup in the same way as if it were the only
395 * language in the app, and it operates in the same way,
396 * the only difference between single language and multi-lang is here, in the
397 * master.
398 *This invisibility to mode is why the language has to use registration calls
399 * for everything during startup -- those calls do different things depending
400 * on whether it's single-language or multi-language mode.
401 *
402 *In this version of the master, work can either be a task or a resumed slave
403 *Having two cases makes this logic complex.. can be finishing either, and
404 * then the next available work may be either.. so really have two distinct
405 * loops that are inter-twined..
406 *
407 *Some special cases:
408 * A task-end is a special case for a few reasons (below).
409 * A task-end can't block a slave (can't cause it to "logically suspend")
410 * A task available for work can only be assigned to a special slave, which
411 * has been set aside for doing tasks, one such task-slave is always
412 * assigned to each slot. So, when a task ends, a new task is assigned to
413 * that slot's task-slave right away.
414 * But if no tasks are available, then have to switch over to looking at
415 * slaves to find one ready to resume, to find work for the slot.
416 * If a task just suspends, not ends, then its task-slave is no longer
417 * available to take new tasks, so a new task-slave has to be assigned to
418 * that slot. Then the slave of the suspended task is turned into a free
419 * task-slave and request handling is done on it as if it were a slave
420 * that suspended.
421 * After request handling, do the same sequence of looking for a task to be
422 * work, and if none, look for a slave ready to resume, as work for the slot.
423 * If a slave suspends, handle its request, then look for work.. first for a
424 * task to assign, and if none, slaves ready to resume.
425 * Another special case is when task-end is done on a free task-slave.. in
426 * that case, the slave has no more work and no way to get more.. so place
427 * it into a recycle queue.
428 * If no work is found of either type, then do a special thing to prune down
429 * the extra slaves in the recycle queue, just so don't get too many..
430 *
431 *The multi-lang thing complicates matters..
432 *
433 *For request handling, it means have to first fetch the semantic environment
434 * of the language, and then do the request handler pointed to by that
435 * semantic env.
436 *For assigning, things get more complex because of competing goals.. One
437 * goal is for language specific stuff to be used during assignment, so
438 * assigner can make higher quality decisions.. but with multiple languages,
439 * which only get mixed in the application, the assigners can't be written
440 * with knowledge of each other. So, they can only make localized decisions,
441 * and so different language's assigners may interfere with each other..
442 *
443 *So, have some possibilities available:
444 *1) can have a fixed scheduler in the proto-runtime, that all the
445 * languages give their work to.. (but then lose language-specific info,
446 * there is a standard PR format for assignment info, and the langauge
447 * attaches this to the work-unit when it gives it to PR.. also have issue
448 * with HWSim, which uses a priority Q instead of FIFO, and requests can
449 * "undo" previous work put in, so request handlers need way to manipulate
450 * the work-holding Q..) (this might be fudgeable with
451 * HWSim, if the master did a lang-supplied callback each time it assigns a
452 * unit to a slot.. then HWSim can keep exactly one unit of work in PR's
453 * queue at a time.. but this is quite hack-like.. or perhaps HWSim supplies
454 * a task-end handler that kicks the next unit of work from HWSim internal
455 * priority queue, over to PR readyQ)
456 *2) can have each language have its own semantic env, that holds its own
457 * work, which is assigned by its own assigner.. then the master searches
458 * through all the semantic envs to find one with work and asks it give work..
459 * (this has downside of blinding assigners to each other.. but does work
460 * for HWSim case)
461 *3) could make PR have a different readyQ for each core, and ask the lang
462 * to put work to the core it prefers.. but the work may be moved by PR if
463 * needed, say if one core idles for too long. This is a hybrid approach,
464 * letting the language decide which core, but PR keeps the work and does it
465 * FIFO style.. (this might als be fudgeable with HWSim, in similar fashion,
466 * but it would be complicated by having to track cores separately)
467 *
468 *Choosing 2, to keep compatibility with single-lang mode.. it allows the same
469 * assigner to be used for single-lang as for multi-lang.. the overhead of
470 * the extra master search for work is part of the price of the flexibility,
471 * but should be fairly small.. takes the first env that has work available,
472 * and whatever it returns is assigned to the slot..
473 *
474 *As a hybrid, giving an option for a unified override assigner to be registered
475 * and used.. This allows something like a static analysis to detect
476 * which languages are grouped together, and then analyze the pattern of
477 * construct calls, and generate a custom assigner that uses info from all
478 * the languages in a unified way.. Don't really expect this to happen,
479 * but making it possible.
480 */
481 #ifdef MODE__MULTI_LANG
482 void animationMaster( void *initData, SlaveVP *masterVP )
483 {
484 //Used while scanning and filling animation slots
485 int32 slotIdx, numSlotsFilled;
486 AnimSlot *currSlot, **animSlots;
487 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
489 //Local copies, for performance
490 MasterEnv *masterEnv;
491 SlaveAssigner slaveAssigner;
492 RequestHandler requestHandler;
493 PRSemEnv *semanticEnv;
494 int32 thisCoresIdx;
496 //#ifdef MODE__MULTI_LANG
497 SlaveVP *slave;
498 PRProcess *process;
499 PRConstrEnvHolder *constrEnvHolder;
500 int32 langMagicNumber;
501 //#endif
503 //======================== Initializations ========================
504 masterEnv = (MasterEnv*)_PRTopEnv;
506 thisCoresIdx = masterVP->coreAnimatedBy;
507 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
509 requestHandler = masterEnv->requestHandler;
510 slaveAssigner = masterEnv->slaveAssigner;
511 semanticEnv = masterEnv->semanticEnv;
513 //initialize, for non-multi-lang, non multi-proc case
514 // default handler gets put into master env by a registration call by lang
515 endTaskHandler = masterEnv->defaultTaskHandler;
517 HOLISTIC__Insert_Master_Global_Vars;
519 //======================== animationMaster ========================
520 //Do loop gets requests handled and work assigned to slots..
521 // work can either be a task or a resumed slave
522 //Having two cases makes this logic complex.. can be finishing either, and
523 // then the next available work may be either.. so really have two distinct
524 // loops that are inter-twined..
525 while(1){
527 MEAS__Capture_Pre_Master_Point
529 //Scan the animation slots
530 numSlotsFilled = 0;
531 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
532 {
533 currSlot = animSlots[ slotIdx ];
535 //Check if newly-done slave in slot, which will need request handled
536 if( currSlot->workIsDone )
537 { currSlot->workIsDone = FALSE;
539 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
540 MEAS__startReqHdlr;
543 //process the request made by the slave (held inside slave struc)
544 slave = currSlot->slaveAssignedToSlot;
546 //check if the completed work was a task..
547 if( slave->taskMetaInfo->isATask )
548 {
549 if( slave->reqst->type == TaskEnd )
550 { //do task end handler, which is registered separately
551 //note, end hdlr may use semantic data from reqst..
552 //#ifdef MODE__MULTI_LANG
553 //get end-task handler
554 //taskEndHandler = lookup( slave->reqst->langMagicNumber, processEnv );
555 taskEndHandler = slave->taskMetaInfo->endTaskHandler;
556 //#endif
557 (*taskEndHandler)( slave, semanticEnv );
559 goto AssignWork;
560 }
561 else //is a task, and just suspended
562 { //turn slot slave into free task slave & make replacement
563 if( slave->typeOfVP == TaskSlotSlv ) changeSlvType();
565 //goto normal slave request handling
566 goto SlaveReqHandling;
567 }
568 }
569 else //is a slave that suspended
570 {
571 SlaveReqHandling:
572 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
574 HOLISTIC__Record_AppResponder_end;
575 MEAS__endReqHdlr;
577 goto AssignWork;
578 }
579 } //if has suspended slave that needs handling
581 //if slot empty, hand to Assigner to fill with a slave
582 if( currSlot->needsSlaveAssigned )
583 { //Call plugin's Assigner to give slot a new slave
584 HOLISTIC__Record_Assigner_start;
586 AssignWork:
588 assignedSlaveVP = assignWork( semanticEnv, currSlot );
590 //put the chosen slave into slot, and adjust flags and state
591 if( assignedSlaveVP != NULL )
592 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
593 assignedSlaveVP->animSlotAssignedTo = currSlot;
594 currSlot->needsSlaveAssigned = FALSE;
595 numSlotsFilled += 1;
596 }
597 else
598 {
599 currSlot->needsSlaveAssigned = TRUE; //local write
600 }
601 HOLISTIC__Record_Assigner_end;
602 }//if slot needs slave assigned
603 }//for( slotIdx..
605 MEAS__Capture_Post_Master_Point;
607 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
608 flushRegisters();
609 }//while(1)
610 }
611 #endif //MODE__MULTI_LANG
615 //This is the master when both multi-lang and multi-process modes are turned on
616 //#ifdef MODE__MULTI_LANG
617 //#ifdef MODE__MULTI_PROCESS
618 void animationMaster( void *initData, SlaveVP *masterVP )
619 {
620 int32 slotIdx;
621 // int32 numSlotsFilled;
622 AnimSlot *currSlot;
623 //Used while scanning and filling animation slots
624 AnimSlot **animSlots;
626 //Local copies, for performance
627 MasterEnv *masterEnv;
628 int32 thisCoresIdx;
630 //======================== Initializations ========================
631 masterEnv = (MasterEnv*)_PRTopEnv;
633 thisCoresIdx = masterVP->coreAnimatedBy;
634 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
636 HOLISTIC__Insert_Master_Global_Vars;
638 //======================== animationMaster ========================
639 //Do loop gets requests handled and work assigned to slots..
640 // work can either be a task or a resumed slave
641 //Having two cases makes this logic complex.. can be finishing either, and
642 // then the next available work may be either.. so really have two distinct
643 // loops that are inter-twined..
644 while(1)
645 {
646 MEAS__Capture_Pre_Master_Point
648 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
649 {
650 currSlot = animSlots[ slotIdx ];
652 masterFunction_multiLang( currSlot );
653 }
655 MEAS__Capture_Post_Master_Point;
657 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
658 flushRegisters();
659 }
660 }
661 #endif //MODE__MULTI_LANG
662 #endif //MODE__MULTI_PROCESS
664 inline
665 void
666 masterFunction_multiLang( AnimSlot *currSlot )
667 { //Scan the animation slots
668 int32 magicNumber;
669 SlaveVP *slave;
670 SlaveVP *assignedSlaveVP;
671 PRSemEnv *semanticEnv;
672 PRReqst *req;
673 RequestHandler requestHandler;
675 //Check if newly-done slave in slot, which will need request handled
676 if( currSlot->workIsDone )
677 { currSlot->workIsDone = FALSE;
679 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
680 MEAS__startReqHdlr;
683 //process the request made by the slave (held inside slave struc)
684 slave = currSlot->slaveAssignedToSlot;
686 //check if the slave was doing a task..
687 //Action depends on both on the request type, and whether it's on
688 // a generic slave vs a suspended task
689 if( slave->metaTask->taskType == AtomicTask ||
690 slave->metaTask->taskType == SuspendedTask )
691 {
692 switch( slave->request->reqType )
693 { case TaskEnd:
694 { PRHandle_EndTask( slave ); //if free task slave, update count, put into recycle Q -- do handler before lang's handler
696 //do task end handler, which is registered separately
697 //note, end hdlr may use semantic data from reqst..
698 //get end-task handler
700 RequestHandler
701 taskEndHandler = slave->metaTask->reqHandler;
702 semanticEnv = PR_int__give_sem_env_for_slave( slave,
703 slave->request->langMagicNumber );
704 (*taskEndHandler)( slave, semanticEnv );
706 goto AssignWork;
707 }
708 case TaskCreate:
709 { PRHandle_CreateTask( slave );
710 justCopied_check;
711 RequestHandler
712 taskCreateHandler = slave->metaTask->reqHandler;
713 semanticEnv = PR_int__give_sem_env_for_slave( slave,
714 slave->request->langMagicNumber );
715 (*taskCreateHandler)( slave, semanticEnv );
717 want_to_resume_creating_slave;
718 goto AssignWork;
719 }
720 default:
721 { //is a task, and just suspended, so tied to a free task slave
722 //First turn slot slave into free task slave & make replacement
723 if( slave->typeOfVP == TaskSlotSlv )
724 replaceWithNewSlotSlv( slave, slave->processSlaveIsIn->processEnv );
726 //goto normal slave request handling
727 goto SlaveReqHandling;
728 }
729 }
730 }
731 else //is a slave that suspended
732 {
734 SlaveReqHandling:
735 //Q: put the switch in inline call, to clean up code?
736 req = slave->request;
737 switch( req->reqType )
738 { case SlvCreate: PRHandle_CreateSlave( slave ); break;
739 case SlvDissipate: PRHandle_Dissipate( slave ); break;
740 case Service: PR_int__handle_PRServiceReq( slave ); break; //resume into PR's own semantic env
741 case Hardware: //for future expansion
742 case IO: //for future expansion
743 case OSCall: //for future expansion
744 case Language: //normal sem request
745 magicNumber = slave->request->langMagicNumber;
746 semanticEnv = PR_PI__give_sem_env_for( slave, magicNumber );
747 requestHandler = semanticEnv->requestHdlr;
748 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
749 }
751 HOLISTIC__Record_AppResponder_end;
752 MEAS__endReqHdlr;
754 goto AssignWork;
755 }
756 } //if has suspended slave that needs handling
758 //End up here when the slot did not have ended work in it (no req)
759 //So, here, if slot empty, look for work to fill the slot
760 if( currSlot->needsSlaveAssigned )
761 { HOLISTIC__Record_Assigner_start;
763 AssignWork:
764 //Scan sem environs, looking for semEnv with ready work.
765 // call the Assigner for that sem Env, to get a slave for the slot
766 assignedSlaveVP = assignWork( semanticEnv, currSlot );
768 //put the chosen slave into slot, and adjust flags and state
769 if( assignedSlaveVP != NULL )
770 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
771 assignedSlaveVP->animSlotAssignedTo = currSlot;
772 currSlot->needsSlaveAssigned = FALSE;
773 }
774 else
775 { currSlot->needsSlaveAssigned = TRUE; //local write
776 }
777 HOLISTIC__Record_Assigner_end;
778 }//if slot needs slave assigned
779 }
781 //==========================================================================
782 /*When a task in a slot slave suspends, the slot slave has to be changed to
783 * a free task slave, then the slot slave replaced. The replacement can be
784 * either a recycled free task slave that finished it's task and has been
785 * idle in the recycle queue, or else create a new slave to be the slot slave.
786 *The master only calls this with a slot slave that needs to be replaced.
787 */
788 inline void
789 replaceWithNewSlotSlv( SlaveVP *requestingSlv, PRProcessEnv *processEnv )
790 { SlaveVP *newSlotSlv;
791 VSsSemData *semData;
793 fixMe__still_VSs_stuff_in_here;
794 //get a new slave to be the slot slave
795 newSlotSlv = readPrivQ( processEnv->freeTaskSlvRecycleQ );
796 if( newSlotSlv == NULL )
797 { newSlotSlv = PR_int__create_slaveVP( &idle_fn, NULL, processEnv, 0);
798 //just made a new free task slave, so count it
799 processEnv->numLiveFreeTaskSlvs += 1;
800 }
802 //set slave values to make it the slot slave
803 newSlotSlv->metaTask = NULL;
804 newSlotSlv->typeOfVP = TaskSlotSlv;
805 newSlotSlv->needsTaskAssigned = TRUE;
807 //a slot slave is pinned to a particular slot on a particular core
808 //Note, this happens before the request is seen by handler, so nothing
809 // has had a chance to change the coreAnimatedBy or anything else..
810 newSlotSlv->animSlotAssignedTo = requestingSlv->animSlotAssignedTo;
811 newSlotSlv->coreAnimatedBy = requestingSlv->coreAnimatedBy;
813 //put it into the slot slave matrix
814 int32 slotNum = requestingSlv->animSlotAssignedTo->slotIdx;
815 int32 coreNum = requestingSlv->coreAnimatedBy;
816 processEnv->slotTaskSlvs[coreNum][slotNum] = newSlotSlv;
818 //Fix up requester, to be an extra slave now (but not an ended one)
819 // because it's active, doesn't go into freeTaskSlvRecycleQ
820 requestingSlv->typeOfVP = FreeTaskSlv;
821 }
825 /*This does:
826 * 1) searches the semantic environments for one with work ready
827 * if finds one, asks its assigner to return work
828 * 2) checks what kind of work: new task, resuming task, resuming slave
829 * if new task, gets the slot slave and assigns task to it and returns slave
830 * else, gets the slave attached to the metaTask and returns that.
831 * 3) if no work found, then prune former task slaves waiting to be recycled.
832 * If no work and no slaves to prune, check for shutdown conditions.
833 *
834 * Semantic env keeps its own work in its own structures, and has its own
835 * assigner. It chooses
836 * However, include a switch that switches-in an override assigner, which
837 * sees all the work in all the semantic env's. This is most likely
838 * generated by static tools and included in the executable. That means it
839 * has to be called via a registered pointer from here. The idea is that
840 * the static tools know which languages are grouped together.. and the
841 * override enables them to generate a custom assigner that uses info from
842 * all the languages in a unified way.. Don't really expect this to happen,
843 * but am making it possible.
844 */
845 inline SlaveVP *
846 assignWork( PRProcess *process, AnimSlot *slot )
847 { SlaveVP *returnSlv;
848 //VSsSemEnv *semEnv;
849 //VSsSemData *semData;
850 int32 coreNum, slotNum;
851 PRMetaTask *newMetaTask, *assignedMetaTask;
852 SlaveVP *freeTaskSlv;
854 coreNum = slot->coreSlotIsOn;
856 if( _PRTopEnv->overrideAssigner != NULL )
857 { assignedMetaTask = (*_PRTopEnv->overrideAssigner)( process, slot );
858 if( assignedMetaTask != NULL )
859 {
860 //have work, so reset Done flag (caused by work generated on other core)
861 if( process->coreIsDone[coreNum] == TRUE ) //reads are higher perf
862 process->coreIsDone[coreNum] = FALSE; //don't just write always
864 switch( assignedMetaTask->taskType )
865 { case GenericSlave: goto AssignSlave;
866 case ResumedTask: goto AssignSlave;
867 case NewTask: goto AssignNewTask;
868 case default: PR_int__throw_exception( "unknown task type ret by assigner" );
869 }
870 }
871 else
872 goto NoWork;
873 }
875 //If here, then no override assigner, so search semantic envs for work
876 int32 envIdx, numEnvs; PRSemEnv **semEnvs, *semEnv; SlaveAssigner assigner;
877 semEnvs = process->semEnvs;
878 numEnvs = process->numSemEnvs;
879 for( envIdx = 0; envIdx < numEnvs; envIdx++ ) //keep semEnvs in hash AND array
880 { semEnv = semEnvs[envIdx];
881 if( semEnv->hasWork )
882 { assigner = semEnv->assigner;
883 assignedMetaTask = (*assigner)( semEnv, slot );
885 //have work, so reset Done flag (caused by work generated on other core)
886 if( process->coreIsDone[coreNum] == TRUE ) //reads are higher perf
887 process->coreIsDone[coreNum] = FALSE; //don't just write always
889 switch( assignedMetaTask->taskType )
890 { case GenericSlave: goto AssignSlave;
891 case ResumedTask: goto AssignSlave;
892 case NewTask: goto AssignNewTask;
893 case default: PR_int__throw_exception( "unknown task type ret by assigner" );
894 }
895 }
896 }
898 NoWork:
899 //No work, if reach here..
900 //no task, so prune the recycle pool of free task slaves
901 freeTaskSlv = readPrivQ( process->freeTaskSlvRecycleQ );
902 if( freeTaskSlv != NULL )
903 { //delete, so that bound the num extras, and deliver shutdown cond
904 deleteExtraneousFreeTaskSlv( freeTaskSlv, process );
905 //then return NULL
906 returnSlv = NULL;
908 goto ReturnTheSlv;
909 }
910 else
911 { //candidate for shutdown.. all extras dissipated, and no tasks
912 // and no ready to resume slaves, so no way to generate
913 // more work (on this core -- other core might have work still)
914 if( process->numLiveFreeTaskSlvs == 0 &&
915 process->numLiveGenericSlvs == 0 )
916 { //This core sees no way to generate more tasks, so say it
917 if( process->coreIsDone[coreNum] == FALSE )
918 { process->numCoresDone += 1;
919 process->coreIsDone[coreNum] = TRUE;
920 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
921 process->shutdownInitiated = TRUE;
923 #else
924 if( process->numCoresDone == NUM_CORES )
925 { //means no cores have work, and none can generate more
926 process->shutdownInitiated = TRUE;
927 }
928 #endif
929 }
930 }
931 //check if shutdown has been initiated by this or other core
932 if( process->shutdownInitiated )
933 { returnSlv = PR_SS__create_shutdown_slave();
934 }
935 else
936 returnSlv = NULL;
938 goto ReturnTheSlv;
939 } //if( freeTaskSlv != NULL )
942 AssignSlave:
943 { //get slave pointed to by meta task.
944 returnSlv = assignedMetaTask->slaveAssignedTo;
946 returnSlv->coreAnimatedBy = coreNum;
948 goto ReturnTheSlv;
949 }
951 AssignNewTask:
952 {
953 //get the slot slave to assign the task to..
954 coreNum = slot->coreSlotIsOn;
955 slotNum = slot->slotIdx;
956 returnSlv = process->slotTaskSlvs[coreNum][slotNum];
958 //point slave to task's function, and mark slave as having task
959 PR_int__reset_slaveVP_to_TopLvlFn( returnSlv,
960 assignedMetaTask->topLevelFn, assignedMetaTask->initData );
961 returnSlv->metaTask = assignedMetaTask;
962 assignedMetaTask->slaveAssignedTo = returnSlv;
963 returnSlv->needsTaskAssigned = FALSE; //slot slave is a "Task" slave type
965 //have work, so reset Done flag, if was set
966 if( process->coreIsDone[coreNum] == TRUE ) //reads are higher perf
967 process->coreIsDone[coreNum] = FALSE; //don't just write always
969 goto ReturnTheSlv;
970 }
973 ReturnTheSlv: //All paths goto here.. to provide single point for holistic..
975 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
976 if( returnSlv == NULL )
977 { returnSlv = process->idleSlv[coreNum][slotNum];
979 //things that would normally happen in resume(), but idle VPs
980 // never go there
981 returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID
982 Unit newU;
983 newU.vp = returnSlv->slaveID;
984 newU.task = returnSlv->numTimesAssignedToASlot;
985 addToListOfArrays(Unit,newU,process->unitList);
987 if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit
988 { Dependency newD; // to this one
989 newD.from_vp = returnSlv->slaveID;
990 newD.from_task = returnSlv->numTimesAssignedToASlot - 1;
991 newD.to_vp = returnSlv->slaveID;
992 newD.to_task = returnSlv->numTimesAssignedToASlot;
993 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList);
994 }
995 }
996 else //have a slave will be assigned to the slot
997 { //assignSlv->numTimesAssigned++;
998 //get previous occupant of the slot
999 Unit prev_in_slot =
1000 process->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum];
1001 if(prev_in_slot.vp != 0) //if not first slave in slot, make dependency
1002 { Dependency newD; // is a hardware dependency
1003 newD.from_vp = prev_in_slot.vp;
1004 newD.from_task = prev_in_slot.task;
1005 newD.to_vp = returnSlv->slaveID;
1006 newD.to_task = returnSlv->numTimesAssignedToASlot;
1007 addToListOfArrays(Dependency,newD,process->hwArcs);
1009 prev_in_slot.vp = returnSlv->slaveID; //make new slave the new previous
1010 prev_in_slot.task = returnSlv->numTimesAssignedToASlot;
1011 process->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum] =
1012 prev_in_slot;
1014 #endif
1016 return( returnSlv );
1020 /*In creator, only PR related things happen, and things in the langlet whose
1021 * creator construct was used.
1022 *Other langlet still gets a chance to create semData -- but by registering a
1023 * "createSemData" handler in the semEnv. When a construct of the langlet
1024 * calls "PR__give_sem_data()", if there is no semData for that langlet,
1025 * the PR will call the creator in the langlet's semEnv, place whatever it
1026 * makes as the semData in that slave for that langlet, and return that semData
1028 *So, as far as counting things, a langlet is only allowed to count creation
1029 * of slaves it creates itself.. may have to change this later.. add a way for
1030 * langlet to register a trigger Fn called each time a slave gets created..
1031 * need more experience with what langlets will do at create time.. think Cilk
1032 * has interesting create behavior.. not sure how that will differ in light
1033 * of true tasks and langlet approach. Look at it after all done and start
1034 * modifying the langs to be langlets..
1036 *PR itself needs to create the slave, then update numLiveSlaves in process,
1037 * copy processID from requestor to newly created
1038 */
1039 PRHandle_CreateSlave( PRReqst *req, SlaveVP *requestingSlv )
1040 { SlaveVP *newSlv;
1041 PRMetaTask metaTask;
1042 PRProcess *process;
1044 process = requestingSlv->processSlaveIsIn;
1045 newSlv = PR_int__create_slaveVP();
1046 newSlv->typeOfVP = GenericSlv;
1047 newSlv->processSlaveIsIn = process;
1048 process->numLiveGenericSlaves += 1;
1049 metaTask = PR_int__create_slave_meta_task();
1050 metaTask->taskID = req->ID;
1051 metaTask->taskType = GenericSlave;
1053 (*req->handler)(newSlv);
1056 /*The dissipate handler has to update the number of slaves of the type, within
1057 * the process, and call the langlet handler linked into the request,
1058 * and after that returns, then call the PR function that frees the slave state
1059 * (or recycles the slave).
1061 *The PR function that frees the slave state has to also free all of the
1062 * semData in the slave.. or else reset all of the semDatas.. by, say, marking
1063 * them, then in PR__give_semData( magicNum ) call the langlet registered
1064 * "resetSemData" Fn.
1065 */
1066 PRHandle_Dissipate( SlaveVP *slave )
1067 { PRProcess *process;
1068 void *semEnv;
1070 process = slave->processSlaveIsIn;
1072 //do the language's dissipate handler
1073 semEnv = PR_int__give_sem_env_for( slave, slave->request->langMagicNumber );
1074 (*slave->request->handler)( slave, semEnv );
1076 process->numLiveGenericSlaves -= 1;
1077 PR_int__dissipate_slaveVP_multilang( slave ); //recycles and resets semDatas
1079 //check End Of Process Condition
1080 if( process->numLiveTasks == 0 &&
1081 process->numLiveGenericSlaves == 0 )
1082 signalEndOfProcess;
1085 /*Create task is a special form, that has PR behavior in addition to plugin
1086 * behavior. Master calls this first, and this in turn calls the plugin's
1087 * create task handler.
1088 */
1089 inline void
1090 PRHandle_CreateTask( TopLevelFn topLevelFn, void *initData, PRReqst *req,
1091 SlaveVP *requestingSlv )
1092 { PRMetaTask *metaTask;
1093 PRProcess *process;
1094 void *semEnv, _langMetaTask;
1095 PRLangMetaTask *langMetaTask;
1097 process = requestingSlv->processSlaveIsIn;
1099 metaTask = PR_int__create_meta_task( req );
1100 metaTask->taskID = req->ID; //may be NULL
1101 metaTask->topLevelFn = topLevelFn;
1102 metaTask->initData = initData;
1104 process->numLiveTasks += 1;
1106 //plugin tracks tasks ready, and has its own assigner, so task doesn't
1107 // come back from lang's handler -- it's consumed and stays in semEnv.
1108 //But handler gives back the language-specific meta-task it creates, and
1109 // then hook that into the PR meta-task
1110 //(Could also do PRMetaTask as a prolog -- make a Fn that takes the size
1111 // of the lang's metaTask, and alloc's that plus the prolog and returns
1112 // ptr to position just above the prolog)
1113 semEnv = PR_int__give_semEnv_of_req( req, requestingSlv ); //magic num in req
1114 _langMetaTask = (*requestingSlv->request->handler)(req, semEnv);
1115 langMetaTask = (PRLangMetaTask *)_langMetaTask;
1116 metaTask->langMetaTask = langMetaTask;
1117 langMetaTask->protoMetaTask = metaTask;
1119 return;
1122 /*When a task ends, are two scenarios: 1) task ran to completion, or 2) task
1123 * suspended at some point in its code.
1124 *For 1, just decr count of live tasks (and check for end condition) -- the
1125 * master loop will decide what goes into the slot freed up by this task end,
1126 * so, here, don't worry about assigning a new task to the slot slave.
1127 *For 2, the task's slot slave has been converted to a free task slave, which
1128 * now has nothing more to do, so send it to the recycle Q (which includes
1129 * freeing all the semData and meta task structs alloc'd for it). Then
1130 * decrement the live task count and check end condition.
1132 *PR has to update count of live tasks, and check end of process condition.
1133 * There are constructs that wait for a process to end, so when end detected,
1134 * have to resume what's waiting..
1135 *Thing is, the wait is used in "main", so it's an OS thread. That means
1136 * PR internals have to do OS thread signaling. Want to do that in the
1137 * core controller, which has the original stack of an OS thread.
1139 *So here, when detect process end, signal to the core controller, which will
1140 * then do the condition variable notify to the OS thread that's waiting.
1141 */
1142 inline void
1143 PRHandle_EndTask( SlaveVP *requestingSlv )
1144 { void *semEnv;
1145 PRReqst *req;
1146 PRMetaTask *metaTask;
1147 PRProcess *process;
1149 req = requestingSlv->request;
1150 semEnv = PR_int__give_semEnv_of_req( req, requestingSlv ); //magic num in req
1151 metaTask = req->metaTask;
1152 //Want to keep PRMetaTask hidden from plugin, so extract semReq..
1153 (*req->handler)( metaTask, req->semReq, semEnv );
1155 recycleFreeTaskSlave( requestingSlv );
1157 process->numLiveTasks -= 1;
1159 //check End Of Process Condition
1160 if( process->numLiveTasks == 0 &&
1161 process->numLiveGenericSlaves == 0 )
1162 signalEndOfProcessToCoreCtlr;