Mercurial > cgi-bin > hgwebdir.cgi > VMS > VMS_Implementations > VMS_impls > VMS__MC_shared_impl
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 |
