VMS/VMS_Implementations/VMS_impls/VMS__MC_shared_impl

view 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
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"
16 /*The animationMaster embodies most of the animator of the language. The
17 * animator is what emodies the behavior of language constructs.
18 * As such, it is the animationMaster, in combination with the plugin
19 * functions, that make the language constructs do their behavior.
20 *
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
23 * refill the animation slots with slaves that have work.
24 *
25 *There are multiple versions of the master, each tuned to a specific
26 * combination of modes. This keeps the master simple, with reduced overhead,
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 */
41 //===================== The versions of the Animation Master =================
42 //
43 //==============================================================================
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).
56 *Each request represents a language construct that has been encountered
57 * by the application code in the slave. Passing the request to the
58 * request handler is how that language construct's behavior gets invoked.
59 * The request handler then performs the actions of the construct's
60 * behavior. So, the request handler encodes the behavior of the
61 * language's parallelism constructs, and performs that when the master
62 * hands it a slave containing a request to perform that construct.
63 *
64 *On a shared-memory machine, the behavior of parallelism constructs
65 * equals control, over order of execution of code. Hence, the behavior
66 * of the language constructs performed by the request handler is to
67 * choose the order that slaves get animated, and thereby control the
68 * order that application code in the slaves executes.
69 *
70 *To control order of animation of slaves, the request handler has a
71 * semantic environment that holds data structures used to hold slaves
72 * and choose when they're ready to be animated.
73 *
74 *Once a slave is marked as ready to be animated by the request handler,
75 * it is the second plugin function, the Assigner, which chooses the core
76 * the slave gets assigned to for animation. Hence, the Assigner doesn't
77 * perform any of the semantic behavior of language constructs, rather
78 * it gives the language a chance to improve performance. The performance
79 * of application code is strongly related to communication between
80 * cores. On shared-memory machines, communication is caused during
81 * execution of code, by memory accesses, and how much depends on contents
82 * of caches connected to the core executing the code. So, the placement
83 * of slaves determines the communication caused during execution of the
84 * slave's code.
85 *The point of the Assigner, then, is to use application information during
86 * execution of the program, to make choices about slave placement onto
87 * cores, with the aim to put slaves close to caches containing the data
88 * used by the slave's code.
89 *
90 *==========================================================================
91 *In summary, the animationMaster scans the slots, finds slaves
92 * just-finished, which hold requests, pass those to the request handler,
93 * along with the semantic environment, and the request handler then manages
94 * the structures in the semantic env, which controls the order of
95 * animation of slaves, and so embodies the behavior of the language
96 * constructs.
97 *The animationMaster then rescans the slots, offering each empty one to
98 * the Assigner, along with the semantic environment. The Assigner chooses
99 * among the ready slaves in the semantic Env, finding the one best suited
100 * to be animated by that slot's associated core.
101 *
102 *==========================================================================
103 *Implementation Details:
104 *
105 *There is a separate masterVP for each core, but a single semantic
106 * environment shared by all cores. Each core also has its own scheduling
107 * slots, which are used to communicate slaves between animationMaster and
108 * coreController. There is only one global variable, _PRMasterEnv, which
109 * holds the semantic env and other things shared by the different
110 * masterVPs. The request handler and Assigner are registered with
111 * the animationMaster by the language's init function, and a pointer to
112 * each is in the _PRMasterEnv. (There are also some pthread related global
113 * vars, but they're only used during init of PR).
114 *PR gains control over the cores by essentially "turning off" the OS's
115 * scheduler, using pthread pin-to-core commands.
116 *
117 *The masterVPs are created during init, with this animationMaster as their
118 * top level function. The masterVPs use the same SlaveVP data structure,
119 * even though they're not slave VPs.
120 *A "seed slave" is also created during init -- this is equivalent to the
121 * "main" function in C, and acts as the entry-point to the PR-language-
122 * based application.
123 *The masterVPs share a single system-wide master-lock, so only one
124 * masterVP may be animated at a time.
125 *The core controllers access _PRMasterEnv to get the masterVP, and when
126 * they start, the slots are all empty, so they run their associated core's
127 * masterVP. The first of those to get the master lock sees the seed slave
128 * in the shared semantic environment, so when it runs the Assigner, that
129 * returns the seed slave, which the animationMaster puts into a scheduling
130 * slot then switches to the core controller. That then switches the core
131 * over to the seed slave, which then proceeds to execute language
132 * constructs to create more slaves, and so on. Each of those constructs
133 * causes the seed slave to suspend, switching over to the core controller,
134 * which eventually switches to the masterVP, which executes the
135 * request handler, which uses PR primitives to carry out the creation of
136 * new slave VPs, which are marked as ready for the Assigner, and so on..
137 *
138 *On animation slots, and system behavior:
139 * A request may linger in an animation slot for a long time while
140 * the slaves in the other slots are animated. This only becomes a problem
141 * when such a request is a choke-point in the constraints, and is needed
142 * to free work for *other* cores. To reduce this occurrence, the number
143 * of animation slots should be kept low. In balance, having multiple
144 * animation slots amortizes the overhead of switching to the masterVP and
145 * executing the animationMaster code, which drives for more than one. In
146 * practice, the best balance should be discovered by profiling.
147 */
148 void animationMaster( void *initData, SlaveVP *masterVP )
149 {
150 //Used while scanning and filling animation slots
151 int32 slotIdx, numSlotsFilled;
152 AnimSlot *currSlot, **animSlots;
153 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
155 //Local copies, for performance
156 MasterEnv *masterEnv;
157 SlaveAssigner slaveAssigner;
158 RequestHandler requestHandler;
159 void *semanticEnv;
160 int32 thisCoresIdx;
162 //======================== Initializations ========================
163 masterEnv = (MasterEnv*)_VMSMasterEnv;
165 thisCoresIdx = masterVP->coreAnimatedBy;
166 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
168 requestHandler = masterEnv->requestHandler;
169 slaveAssigner = masterEnv->slaveAssigner;
170 semanticEnv = masterEnv->semanticEnv;
172 HOLISTIC__Insert_Master_Global_Vars;
174 //======================== animationMaster ========================
175 while(1){
177 MEAS__Capture_Pre_Master_Point
179 //Scan the animation slots
180 numSlotsFilled = 0;
181 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
182 {
183 currSlot = animSlots[ slotIdx ];
185 //Check if newly-done slave in slot, which will need request handled
186 if( currSlot->workIsDone )
187 {
188 currSlot->workIsDone = FALSE;
189 currSlot->needsSlaveAssigned = TRUE;
191 HOLISTIC__Record_AppResponder_start;
192 MEAS__startReqHdlr;
194 currSlot->workIsDone = FALSE;
195 currSlot->needsSlaveAssigned = TRUE;
196 SlaveVP *currSlave = currSlot->slaveAssignedToSlot;
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;
209 //Language handles request, which is held inside slave struc
210 (*requestHandler)( currSlave, semanticEnv );
212 MEAS__endReqHdlr;
213 }
214 }
216 //process the requests made by the slave (held inside slave struc)
217 (*requestHandler)( currSlot->slaveAssignedToSlot, semanticEnv );
219 HOLISTIC__Record_AppResponder_end;
220 MEAS__endReqHdlr;
221 }
222 //If slot empty, hand to Assigner to fill with a slave
223 if( currSlot->needsSlaveAssigned )
224 { //Call plugin's Assigner to give slot a new slave
225 HOLISTIC__Record_Assigner_start;
226 assignedSlaveVP =
227 (*slaveAssigner)( semanticEnv, currSlot );
229 //put the chosen slave into slot, and adjust flags and state
230 if( assignedSlaveVP != NULL )
231 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
232 assignedSlaveVP->animSlotAssignedTo = currSlot;
233 currSlot->needsSlaveAssigned = FALSE;
234 numSlotsFilled += 1;
236 HOLISTIC__Record_Assigner_end;
237 }
238 }
239 }
241 MEAS__Capture_Post_Master_Point;
243 masterSwitchToCoreCtlr( masterVP );
244 flushRegisters();
245 DEBUG__printf(FALSE,"came back after switch to core -- so lock released!");
246 }//while(1)
247 }
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
260 //Local copies, for performance
261 MasterEnv *masterEnv;
262 SlaveAssigner slaveAssigner;
263 RequestHandler requestHandler;
264 PRSemEnv *semanticEnv;
265 int32 thisCoresIdx;
267 //#ifdef MODE__MULTI_LANG
268 SlaveVP *slave;
269 PRProcess *process;
270 PRConstrEnvHolder *constrEnvHolder;
271 int32 langMagicNumber;
272 //#endif
274 //======================== Initializations ========================
275 masterEnv = (MasterEnv*)_PRMasterEnv;
277 thisCoresIdx = masterVP->coreAnimatedBy;
278 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
280 requestHandler = masterEnv->requestHandler;
281 slaveAssigner = masterEnv->slaveAssigner;
282 semanticEnv = masterEnv->semanticEnv;
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;
288 HOLISTIC__Insert_Master_Global_Vars;
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){
298 MEAS__Capture_Pre_Master_Point
300 //Scan the animation slots
301 numSlotsFilled = 0;
302 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
303 {
304 currSlot = animSlots[ slotIdx ];
306 //Check if newly-done slave in slot, which will need request handled
307 if( currSlot->workIsDone )
308 { currSlot->workIsDone = FALSE;
310 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
311 MEAS__startReqHdlr;
314 //process the request made by the slave (held inside slave struc)
315 slave = currSlot->slaveAssignedToSlot;
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 );
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();
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)
345 HOLISTIC__Record_AppResponder_end;
346 MEAS__endReqHdlr;
348 goto AssignWork;
349 }
350 } //if has suspended slave that needs handling
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;
357 AssignWork:
359 assignedSlaveVP = assignWork( semanticEnv, currSlot );
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..
376 MEAS__Capture_Post_Master_Point;
378 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
379 flushRegisters();
380 }//while(1)
381 }
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
486 //Local copies, for performance
487 MasterEnv *masterEnv;
488 SlaveAssigner slaveAssigner;
489 RequestHandler requestHandler;
490 PRSemEnv *semanticEnv;
491 int32 thisCoresIdx;
493 //#ifdef MODE__MULTI_LANG
494 SlaveVP *slave;
495 PRProcess *process;
496 PRConstrEnvHolder *constrEnvHolder;
497 int32 langMagicNumber;
498 //#endif
500 //======================== Initializations ========================
501 masterEnv = (MasterEnv*)_PRMasterEnv;
503 thisCoresIdx = masterVP->coreAnimatedBy;
504 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
506 requestHandler = masterEnv->requestHandler;
507 slaveAssigner = masterEnv->slaveAssigner;
508 semanticEnv = masterEnv->semanticEnv;
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;
514 HOLISTIC__Insert_Master_Global_Vars;
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){
524 MEAS__Capture_Pre_Master_Point
526 //Scan the animation slots
527 numSlotsFilled = 0;
528 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
529 {
530 currSlot = animSlots[ slotIdx ];
532 //Check if newly-done slave in slot, which will need request handled
533 if( currSlot->workIsDone )
534 { currSlot->workIsDone = FALSE;
536 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
537 MEAS__startReqHdlr;
540 //process the request made by the slave (held inside slave struc)
541 slave = currSlot->slaveAssignedToSlot;
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 );
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();
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)
571 HOLISTIC__Record_AppResponder_end;
572 MEAS__endReqHdlr;
574 goto AssignWork;
575 }
576 } //if has suspended slave that needs handling
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;
583 AssignWork:
585 assignedSlaveVP = assignWork( semanticEnv, currSlot );
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..
602 MEAS__Capture_Post_Master_Point;
604 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
605 flushRegisters();
606 }//while(1)
607 }
608 #endif //MODE__MULTI_LANG
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
622 //Local copies, for performance
623 MasterEnv *masterEnv;
624 SlaveAssigner slaveAssigner;
625 RequestHandler requestHandler;
626 PRSemEnv *semanticEnv;
627 int32 thisCoresIdx;
629 SlaveVP *slave;
630 PRProcess *process;
631 PRConstrEnvHolder *constrEnvHolder;
632 int32 langMagicNumber;
634 //======================== Initializations ========================
635 masterEnv = (MasterEnv*)_PRMasterEnv;
637 thisCoresIdx = masterVP->coreAnimatedBy;
638 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
640 requestHandler = masterEnv->requestHandler;
641 slaveAssigner = masterEnv->slaveAssigner;
642 semanticEnv = masterEnv->semanticEnv;
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;
648 HOLISTIC__Insert_Master_Global_Vars;
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){
658 MEAS__Capture_Pre_Master_Point
660 //Scan the animation slots
661 numSlotsFilled = 0;
662 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
663 {
664 currSlot = animSlots[ slotIdx ];
666 //Check if newly-done slave in slot, which will need request handled
667 if( currSlot->workIsDone )
668 { currSlot->workIsDone = FALSE;
670 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
671 MEAS__startReqHdlr;
674 //process the request made by the slave (held inside slave struc)
675 slave = currSlot->slaveAssignedToSlot;
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;
687 (*taskEndHandler)( slave, semanticEnv );
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();
695 //goto normal slave request handling
696 goto SlaveReqHandling;
697 }
698 }
699 else //is a slave that suspended
700 {
702 SlaveReqHandling:
703 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
705 HOLISTIC__Record_AppResponder_end;
706 MEAS__endReqHdlr;
708 goto AssignWork;
709 }
710 } //if has suspended slave that needs handling
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;
718 AssignWork:
720 assignedSlaveVP = assignWork( semanticEnv, currSlot );
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..
737 MEAS__Capture_Post_Master_Point;
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
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;
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 );
781 return retTaskMetaInfo; //quit, have work
782 }
783 }
785 coreNum = slot->coreSlotIsOn;
786 slotNum = slot->slotIdx;
788 //first try to get a ready slave
789 returnSlv = getReadySlave();
791 if( returnSlv != NULL )
792 { returnSlv->coreAnimatedBy = coreNum;
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
798 goto ReturnTheSlv;
799 }
801 //were no slaves, so try to get a ready task..
802 newTaskStub = getTaskStub();
804 if( newTaskStub != NULL )
805 {
806 //get the slot slave to assign the task to..
807 returnSlv = processEnv->slotTaskSlvs[coreNum][slotNum];
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
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
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;
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;
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;
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
867 ReturnTheSlv: //All paths goto here.. to provide single point for holistic..
869 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
870 if( returnSlv == NULL )
871 { returnSlv = processEnv->idleSlv[coreNum][slotNum];
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);
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
910 return( returnSlv );
911 }
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 }
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
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;
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
979 assignedSlaveVP =
980 (*slaveAssigner)( semanticEnv, currSlot );
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;
989 HOLISTIC__Record_Assigner_end;
990 }
991 }//if slot needs slave assigned
992 }//for( slotIdx..
994 MEAS__Capture_Post_Master_Point;
996 masterSwitchToCoreCtlr( masterVP );
997 flushRegisters();
998 DEBUG__printf(FALSE,"came back after switch to core -- so lock released!");
999 }//while(1)