annotate AnimationMaster.c @ 266:a5fa1e087c7e

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