seanhalle@230: /* seanhalle@268: * Copyright 2010 OpenSourceResearchInstitute seanhalle@230: * seanhalle@230: * Licensed under BSD seanhalle@230: */ seanhalle@230: seanhalle@230: seanhalle@230: seanhalle@230: #include seanhalle@230: #include seanhalle@230: seanhalle@260: #include "PR.h" seanhalle@261: #include "VSs_impl/VSs.h" seanhalle@230: seanhalle@268: /* seanhalle@268: void PRHandle_CreateTask_SL(SlaveVP *slave); seanhalle@230: seanhalle@268: void PRHandle_CreateSlave_SL(SlaveVP *slave); seanhalle@268: void PRHandle_Dissipate_SL(SlaveVP *slave); seanhalle@268: void PR_int__handle_PRServiceReq_SL(SlaveVP *slave); seanhalle@268: */ seanhalle@268: inline void PRHandle_CreateTask( PRReqst *req, SlaveVP *slave ); seanhalle@268: inline void PRHandle_EndTask( PRReqst *req, SlaveVP *slave ); seanhalle@268: inline void PRHandle_CreateSlave(PRReqst *req, SlaveVP *slave ); seanhalle@268: void PRHandle_Dissipate( PRReqst *req, SlaveVP *slave ); seanhalle@268: seanhalle@268: seanhalle@268: //inline void masterFunction_SingleLang( PRLangEnv *protoLangEnv, AnimSlot *slot ); seanhalle@268: inline void masterFunction_MultiLang( AnimSlot *slot ); seanhalle@268: inline PRProcess * pickAProcess( AnimSlot *slot ); seanhalle@268: inline SlaveVP * assignWork( PRProcess *process, AnimSlot *slot ); seanhalle@230: seanhalle@230: /*The animationMaster embodies most of the animator of the language. The seanhalle@230: * animator is what emodies the behavior of language constructs. seanhalle@230: * As such, it is the animationMaster, in combination with the plugin seanhalle@230: * functions, that make the language constructs do their behavior. seanhalle@230: * seanhalle@230: *Within the code, this is the top-level-function of the masterVPs, and seanhalle@230: * runs when the coreController has no more slave VPs. It's job is to seanhalle@260: * refill the animation slots with slaves that have work. seanhalle@230: * seanhalle@260: *There are multiple versions of the master, each tuned to a specific seanhalle@260: * combination of modes. This keeps the master simple, with reduced overhead, seanhalle@260: * when the application is not using the extra complexity. seanhalle@260: * seanhalle@260: *As of Sept 2012, the versions available will be: seanhalle@260: * 1) Single langauge, which only exposes slaves (such as SSR or Vthread) seanhalle@260: * 2) Single language, which only exposes tasks (such as pure dataflow) seanhalle@260: * 3) Single language, which exposes both (like Cilk, StarSs, and OpenMP) seanhalle@260: * 4) Multi-language, which always assumes both tasks and slaves seanhalle@260: * 5) Multi-language and multi-process, which also assumes both tasks and slaves seanhalle@260: * seanhalle@260: * seanhalle@260: * seanhalle@260: */ seanhalle@260: seanhalle@268: //This version of the master selects one of three loops, depending upon seanhalle@268: // whether stand-alone single language (just slaves), or standalone with seanhalle@268: // tasks, or multi-lang (implies multi-process) seanhalle@268: void animationMaster( void *_environment, SlaveVP *masterVP ) seanhalle@268: { seanhalle@268: TopEnv *masterEnv = (TopEnv *)_environment; seanhalle@268: int32 slotIdx; seanhalle@268: AnimSlot *currSlot; seanhalle@268: //Used while scanning and filling animation slots seanhalle@268: AnimSlot **animSlots; seanhalle@268: seanhalle@268: //Local copies, for performance seanhalle@268: int32 thisCoresIdx; seanhalle@268: seanhalle@268: //======================== Initializations ======================== seanhalle@268: thisCoresIdx = masterVP->coreAnimatedBy; seanhalle@268: animSlots = masterEnv->allAnimSlots[thisCoresIdx]; seanhalle@268: seanhalle@268: HOLISTIC__Insert_Master_Global_Vars; seanhalle@268: seanhalle@268: //======================== animationMaster ======================== seanhalle@268: //Have three different modes, and the master behavior is different for seanhalle@268: // each, so jump to the loop that corresponds to the mode. seanhalle@268: // seanhalle@268: switch(masterEnv->mode) seanhalle@268: { seanhalle@268: /* seanhalle@268: { case SingleLang: seanhalle@268: while(1) seanhalle@268: { MEAS__Capture_Pre_Master_Point seanhalle@268: for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++) seanhalle@268: { seanhalle@268: currSlot = animSlots[ slotIdx ]; seanhalle@268: seanhalle@268: masterFunction_StandaloneSlavesOnly( masterEnv, currSlot ); seanhalle@268: } seanhalle@268: MEAS__Capture_Post_Master_Point; seanhalle@268: masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master seanhalle@268: flushRegisters(); seanhalle@268: } seanhalle@268: seanhalle@268: case SingleLang: seanhalle@268: { PRLangEnv *protoLangEnv = _PRTopEnv->protoLangEnv; seanhalle@268: while(1) seanhalle@268: { MEAS__Capture_Pre_Master_Point seanhalle@268: for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++) seanhalle@268: { seanhalle@268: currSlot = animSlots[ slotIdx ]; seanhalle@268: seanhalle@268: masterFunction_SingleLang( protoLangEnv, currSlot ); seanhalle@268: } seanhalle@268: MEAS__Capture_Post_Master_Point; seanhalle@268: masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master seanhalle@268: flushRegisters(); seanhalle@268: } seanhalle@268: } seanhalle@268: */ seanhalle@268: case MultiLang: seanhalle@268: { while(1) seanhalle@268: { MEAS__Capture_Pre_Master_Point seanhalle@268: for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++) seanhalle@268: { seanhalle@268: currSlot = animSlots[ slotIdx ]; seanhalle@268: seanhalle@268: masterFunction_MultiLang( currSlot ); seanhalle@268: } seanhalle@268: MEAS__Capture_Post_Master_Point; seanhalle@268: masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master seanhalle@268: flushRegisters(); seanhalle@268: } seanhalle@268: } seanhalle@268: } seanhalle@268: } seanhalle@268: seanhalle@268: seanhalle@261: seanhalle@260: //===================== The versions of the Animation Master ================= seanhalle@260: // seanhalle@260: //============================================================================== seanhalle@260: seanhalle@260: /* 1) This version is for a single language, that has only slaves, no tasks, seanhalle@260: * such as Vthread or SSR. seanhalle@260: *This version is for when an application has only a single language, and seanhalle@260: * that language exposes slaves explicitly (as opposed to a task based seanhalle@260: * language like pure dataflow). seanhalle@260: * seanhalle@260: * seanhalle@260: *It scans the animation slots for just-completed slaves. seanhalle@260: * Each completed slave has a request in it. So, the master hands each to seanhalle@260: * the plugin's request handler (there is only one plugin, because only one seanhalle@260: * lang). seanhalle@230: *Each request represents a language construct that has been encountered seanhalle@230: * by the application code in the slave. Passing the request to the seanhalle@230: * request handler is how that language construct's behavior gets invoked. seanhalle@230: * The request handler then performs the actions of the construct's seanhalle@230: * behavior. So, the request handler encodes the behavior of the seanhalle@230: * language's parallelism constructs, and performs that when the master seanhalle@230: * hands it a slave containing a request to perform that construct. seanhalle@230: * seanhalle@230: *On a shared-memory machine, the behavior of parallelism constructs seanhalle@230: * equals control, over order of execution of code. Hence, the behavior seanhalle@230: * of the language constructs performed by the request handler is to seanhalle@230: * choose the order that slaves get animated, and thereby control the seanhalle@230: * order that application code in the slaves executes. seanhalle@230: * seanhalle@230: *To control order of animation of slaves, the request handler has a seanhalle@268: * language environment that holds data structures used to hold slaves seanhalle@230: * and choose when they're ready to be animated. seanhalle@230: * seanhalle@230: *Once a slave is marked as ready to be animated by the request handler, seanhalle@230: * it is the second plugin function, the Assigner, which chooses the core seanhalle@230: * the slave gets assigned to for animation. Hence, the Assigner doesn't seanhalle@230: * perform any of the semantic behavior of language constructs, rather seanhalle@230: * it gives the language a chance to improve performance. The performance seanhalle@230: * of application code is strongly related to communication between seanhalle@230: * cores. On shared-memory machines, communication is caused during seanhalle@230: * execution of code, by memory accesses, and how much depends on contents seanhalle@230: * of caches connected to the core executing the code. So, the placement seanhalle@230: * of slaves determines the communication caused during execution of the seanhalle@230: * slave's code. seanhalle@230: *The point of the Assigner, then, is to use application information during seanhalle@230: * execution of the program, to make choices about slave placement onto seanhalle@230: * cores, with the aim to put slaves close to caches containing the data seanhalle@230: * used by the slave's code. seanhalle@230: * seanhalle@230: *========================================================================== seanhalle@230: *In summary, the animationMaster scans the slots, finds slaves seanhalle@230: * just-finished, which hold requests, pass those to the request handler, seanhalle@268: * along with the language environment, and the request handler then manages seanhalle@268: * the structures in the language env, which controls the order of seanhalle@230: * animation of slaves, and so embodies the behavior of the language seanhalle@230: * constructs. seanhalle@230: *The animationMaster then rescans the slots, offering each empty one to seanhalle@268: * the Assigner, along with the language environment. The Assigner chooses seanhalle@268: * among the ready slaves in the language env, finding the one best suited seanhalle@230: * to be animated by that slot's associated core. seanhalle@230: * seanhalle@230: *========================================================================== seanhalle@230: *Implementation Details: seanhalle@230: * seanhalle@268: *There is a separate masterVP for each core, but a single language seanhalle@230: * environment shared by all cores. Each core also has its own scheduling seanhalle@230: * slots, which are used to communicate slaves between animationMaster and seanhalle@261: * coreController. There is only one global variable, _PRTopEnv, which seanhalle@268: * holds the language env and other things shared by the different seanhalle@230: * masterVPs. The request handler and Assigner are registered with seanhalle@230: * the animationMaster by the language's init function, and a pointer to seanhalle@261: * each is in the _PRTopEnv. (There are also some pthread related global seanhalle@260: * vars, but they're only used during init of PR). seanhalle@260: *PR gains control over the cores by essentially "turning off" the OS's seanhalle@230: * scheduler, using pthread pin-to-core commands. seanhalle@230: * seanhalle@230: *The masterVPs are created during init, with this animationMaster as their seanhalle@230: * top level function. The masterVPs use the same SlaveVP data structure, seanhalle@230: * even though they're not slave VPs. seanhalle@230: *A "seed slave" is also created during init -- this is equivalent to the seanhalle@260: * "main" function in C, and acts as the entry-point to the PR-language- seanhalle@230: * based application. seanhalle@260: *The masterVPs share a single system-wide master-lock, so only one seanhalle@230: * masterVP may be animated at a time. seanhalle@261: *The core controllers access _PRTopEnv to get the masterVP, and when seanhalle@230: * they start, the slots are all empty, so they run their associated core's seanhalle@230: * masterVP. The first of those to get the master lock sees the seed slave seanhalle@268: * in the shared language environment, so when it runs the Assigner, that seanhalle@230: * returns the seed slave, which the animationMaster puts into a scheduling seanhalle@230: * slot then switches to the core controller. That then switches the core seanhalle@230: * over to the seed slave, which then proceeds to execute language seanhalle@230: * constructs to create more slaves, and so on. Each of those constructs seanhalle@230: * causes the seed slave to suspend, switching over to the core controller, seanhalle@230: * which eventually switches to the masterVP, which executes the seanhalle@260: * request handler, which uses PR primitives to carry out the creation of seanhalle@230: * new slave VPs, which are marked as ready for the Assigner, and so on.. seanhalle@230: * seanhalle@230: *On animation slots, and system behavior: seanhalle@260: * A request may linger in an animation slot for a long time while seanhalle@230: * the slaves in the other slots are animated. This only becomes a problem seanhalle@230: * when such a request is a choke-point in the constraints, and is needed seanhalle@260: * to free work for *other* cores. To reduce this occurrence, the number seanhalle@230: * of animation slots should be kept low. In balance, having multiple seanhalle@230: * animation slots amortizes the overhead of switching to the masterVP and seanhalle@230: * executing the animationMaster code, which drives for more than one. In seanhalle@230: * practice, the best balance should be discovered by profiling. seanhalle@230: */ seanhalle@268: /* seanhalle@268: void masterFunction_StandaloneSlavesOnly( AnimSlot *slot ) seanhalle@230: { seanhalle@268: SlaveVP *slave; seanhalle@268: PRReqst *req; seanhalle@268: PRLangEnv *langEnv = _PRTopEnv->langEnv; seanhalle@268: seanhalle@230: seanhalle@230: //======================== animationMaster ======================== seanhalle@268: seanhalle@268: //Check if newly-done slave in slot, which will need request handled seanhalle@268: if( slot->workIsDone ) seanhalle@268: { slot->workIsDone = FALSE; seanhalle@268: slot->needsWorkAssigned = TRUE; seanhalle@230: seanhalle@230: seanhalle@268: HOLISTIC__Record_AppResponder_start; seanhalle@268: MEAS__startReqHdlr; seanhalle@268: //process the request made by the slave (held inside slave struc) seanhalle@268: slave = slot->slaveAssignedToSlot; seanhalle@268: req = slave->request; seanhalle@260: seanhalle@268: //Handle task create and end first -- they're special cases.. seanhalle@268: switch( req->reqType ) seanhalle@268: { case SlvCreate: PRHandle_CreateSlave( slave ); break; seanhalle@268: case SlvDissipate: PRHandle_Dissipate( slave ); break; seanhalle@268: case Service: PR_int__handle_PRServiceReq( slave ); break; //resume into PR's own language env seanhalle@268: case Hardware: //for future expansion seanhalle@268: case IO: //for future expansion seanhalle@268: case OSCall: //for future expansion seanhalle@268: PR_int__throw_exception("Not implemented"); break; seanhalle@268: case Language: //normal lang request seanhalle@268: { seanhalle@268: (*langEnv->requestHdlr)( req->langReq, slave, langEnv ); seanhalle@230: } seanhalle@230: } seanhalle@268: HOLISTIC__Record_AppResponder_end; seanhalle@268: MEAS__endReqHdlr; seanhalle@230: } seanhalle@268: //If slot empty, hand to Assigner to fill with a slave seanhalle@268: if( slot->needsWorkAssigned ) seanhalle@268: { //Call plugin's Assigner to give slot a new slave seanhalle@268: HOLISTIC__Record_Assigner_start; seanhalle@230: seanhalle@268: if( langEnv->hasWork ) seanhalle@268: { (*langEnv->slaveAssigner)( langEnv, slot ); //calls PR fn that inserts work into slot seanhalle@268: goto ReturnAfterAssigningWork; //quit for-loop, cause found work seanhalle@268: } seanhalle@268: else seanhalle@268: goto NoWork; seanhalle@268: } seanhalle@230: seanhalle@268: NoWork: seanhalle@268: //No work, if reach here.. seanhalle@268: { seanhalle@268: #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC seanhalle@268: coreNum = slot->coreSlotIsOn; seanhalle@268: returnSlv = process->idleSlv[coreNum][slotNum]; seanhalle@268: seanhalle@268: //things that would normally happen in resume(), but idle VPs seanhalle@268: // never go there seanhalle@268: returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID seanhalle@268: Unit newU; seanhalle@268: newU.vp = returnSlv->slaveNum; seanhalle@268: newU.task = returnSlv->numTimesAssignedToASlot; seanhalle@268: addToListOfArrays(Unit,newU,process->unitList); seanhalle@268: seanhalle@268: if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit seanhalle@268: { Dependency newD; // to this one seanhalle@268: newD.from_vp = returnSlv->slaveNum; seanhalle@268: newD.from_task = returnSlv->numTimesAssignedToASlot - 1; seanhalle@268: newD.to_vp = returnSlv->slaveNum; seanhalle@268: newD.to_task = returnSlv->numTimesAssignedToASlot; seanhalle@268: addToListOfArrays(Dependency, newD ,process->ctlDependenciesList); seanhalle@268: } seanhalle@268: #endif seanhalle@268: HOLISTIC__Record_Assigner_end; seanhalle@268: return; seanhalle@268: } seanhalle@268: seanhalle@268: ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic.. seanhalle@268: { seanhalle@268: HOLISTIC__Record_Assigner_end; seanhalle@268: return; seanhalle@268: } seanhalle@230: } seanhalle@268: */ seanhalle@260: seanhalle@260: seanhalle@260: /*This is the master when just multi-lang, but not multi-process mode is on. seanhalle@260: * This version has to handle both tasks and slaves, and do extra work of seanhalle@268: * looking up the language env and handlers to use, for each completed bit of seanhalle@260: * work. seanhalle@268: *It also has to search through the language envs to find one with work, seanhalle@260: * then ask that env's assigner to return a unit of that work. seanhalle@260: * seanhalle@260: *The language is written to startup in the same way as if it were the only seanhalle@260: * language in the app, and it operates in the same way, seanhalle@260: * the only difference between single language and multi-lang is here, in the seanhalle@260: * master. seanhalle@260: *This invisibility to mode is why the language has to use registration calls seanhalle@260: * for everything during startup -- those calls do different things depending seanhalle@260: * on whether it's single-language or multi-language mode. seanhalle@260: * seanhalle@260: *In this version of the master, work can either be a task or a resumed slave seanhalle@260: *Having two cases makes this logic complex.. can be finishing either, and seanhalle@260: * then the next available work may be either.. so really have two distinct seanhalle@260: * loops that are inter-twined.. seanhalle@260: * seanhalle@260: *Some special cases: seanhalle@260: * A task-end is a special case for a few reasons (below). seanhalle@260: * A task-end can't block a slave (can't cause it to "logically suspend") seanhalle@260: * A task available for work can only be assigned to a special slave, which seanhalle@260: * has been set aside for doing tasks, one such task-slave is always seanhalle@260: * assigned to each slot. So, when a task ends, a new task is assigned to seanhalle@260: * that slot's task-slave right away. seanhalle@260: * But if no tasks are available, then have to switch over to looking at seanhalle@260: * slaves to find one ready to resume, to find work for the slot. seanhalle@260: * If a task just suspends, not ends, then its task-slave is no longer seanhalle@260: * available to take new tasks, so a new task-slave has to be assigned to seanhalle@260: * that slot. Then the slave of the suspended task is turned into a free seanhalle@260: * task-slave and request handling is done on it as if it were a slave seanhalle@260: * that suspended. seanhalle@260: * After request handling, do the same sequence of looking for a task to be seanhalle@260: * work, and if none, look for a slave ready to resume, as work for the slot. seanhalle@260: * If a slave suspends, handle its request, then look for work.. first for a seanhalle@260: * task to assign, and if none, slaves ready to resume. seanhalle@260: * Another special case is when task-end is done on a free task-slave.. in seanhalle@260: * that case, the slave has no more work and no way to get more.. so place seanhalle@260: * it into a recycle queue. seanhalle@260: * If no work is found of either type, then do a special thing to prune down seanhalle@260: * the extra slaves in the recycle queue, just so don't get too many.. seanhalle@260: * seanhalle@260: *The multi-lang thing complicates matters.. seanhalle@260: * seanhalle@268: *For request handling, it means have to first fetch the language environment seanhalle@260: * of the language, and then do the request handler pointed to by that seanhalle@268: * language env. seanhalle@260: *For assigning, things get more complex because of competing goals.. One seanhalle@260: * goal is for language specific stuff to be used during assignment, so seanhalle@260: * assigner can make higher quality decisions.. but with multiple languages, seanhalle@260: * which only get mixed in the application, the assigners can't be written seanhalle@260: * with knowledge of each other. So, they can only make localized decisions, seanhalle@260: * and so different language's assigners may interfere with each other.. seanhalle@260: * seanhalle@260: *So, have some possibilities available: seanhalle@260: *1) can have a fixed scheduler in the proto-runtime, that all the seanhalle@260: * languages give their work to.. (but then lose language-specific info, seanhalle@260: * there is a standard PR format for assignment info, and the langauge seanhalle@260: * attaches this to the work-unit when it gives it to PR.. also have issue seanhalle@260: * with HWSim, which uses a priority Q instead of FIFO, and requests can seanhalle@260: * "undo" previous work put in, so request handlers need way to manipulate seanhalle@260: * the work-holding Q..) (this might be fudgeable with seanhalle@260: * HWSim, if the master did a lang-supplied callback each time it assigns a seanhalle@260: * unit to a slot.. then HWSim can keep exactly one unit of work in PR's seanhalle@260: * queue at a time.. but this is quite hack-like.. or perhaps HWSim supplies seanhalle@260: * a task-end handler that kicks the next unit of work from HWSim internal seanhalle@260: * priority queue, over to PR readyQ) seanhalle@268: *2) can have each language have its own language env, that holds its own seanhalle@260: * work, which is assigned by its own assigner.. then the master searches seanhalle@268: * through all the language envs to find one with work and asks it give work.. seanhalle@260: * (this has downside of blinding assigners to each other.. but does work seanhalle@260: * for HWSim case) seanhalle@260: *3) could make PR have a different readyQ for each core, and ask the lang seanhalle@260: * to put work to the core it prefers.. but the work may be moved by PR if seanhalle@260: * needed, say if one core idles for too long. This is a hybrid approach, seanhalle@260: * letting the language decide which core, but PR keeps the work and does it seanhalle@260: * FIFO style.. (this might als be fudgeable with HWSim, in similar fashion, seanhalle@260: * but it would be complicated by having to track cores separately) seanhalle@260: * seanhalle@260: *Choosing 2, to keep compatibility with single-lang mode.. it allows the same seanhalle@260: * assigner to be used for single-lang as for multi-lang.. the overhead of seanhalle@260: * the extra master search for work is part of the price of the flexibility, seanhalle@260: * but should be fairly small.. takes the first env that has work available, seanhalle@260: * and whatever it returns is assigned to the slot.. seanhalle@260: * seanhalle@260: *As a hybrid, giving an option for a unified override assigner to be registered seanhalle@260: * and used.. This allows something like a static analysis to detect seanhalle@260: * which languages are grouped together, and then analyze the pattern of seanhalle@260: * construct calls, and generate a custom assigner that uses info from all seanhalle@260: * the languages in a unified way.. Don't really expect this to happen, seanhalle@260: * but making it possible. seanhalle@260: */ seanhalle@268: /* seanhalle@268: inline seanhalle@268: void seanhalle@268: masterFunction_SingleLang( PRLangEnv *protoLangEnv, AnimSlot *slot ) seanhalle@268: { //Scan the animation slots seanhalle@268: SlaveVP *slave; seanhalle@268: PRReqst *req; seanhalle@268: seanhalle@268: //Check if newly-done slave in slot, which will need request handled seanhalle@268: if( slot->workIsDone ) seanhalle@268: { slot->workIsDone = FALSE; seanhalle@268: slot->needsWorkAssigned = TRUE; seanhalle@268: seanhalle@268: HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot seanhalle@268: MEAS__startReqHdlr; seanhalle@268: seanhalle@268: seanhalle@268: //process the request made by the slave (held inside slave struc) seanhalle@268: slave = slot->slaveAssignedToSlot; seanhalle@268: req = slave->request; seanhalle@268: seanhalle@268: //If the requesting slave is a slot slave, and request is not seanhalle@268: // task-end, then turn it into a free task slave. seanhalle@268: if( slave->typeOfVP == SlotTaskSlv && req->reqType != TaskEnd ) seanhalle@268: PR_int__replace_with_new_slot_slv( slave ); seanhalle@268: seanhalle@268: //Handle task create and end first -- they're special cases.. seanhalle@268: switch( req->reqType ) seanhalle@268: { case TaskEnd: seanhalle@268: { //do PR handler, which calls lang's hdlr and does recycle of seanhalle@268: // free task slave if needed -- PR handler checks for free task Slv seanhalle@268: PRHandle_EndTask_SL( slave ); break; seanhalle@268: } seanhalle@268: case TaskCreate: seanhalle@268: { //Do PR's create-task handler, which calls the lang's hdlr seanhalle@268: // PR handler checks for free task Slv seanhalle@268: PRHandle_CreateTask_SL( slave ); break; seanhalle@268: } seanhalle@268: case SlvCreate: PRHandle_CreateSlave_SL( slave ); break; seanhalle@268: case SlvDissipate: PRHandle_Dissipate_SL( slave ); break; seanhalle@268: case Service: PR_int__handle_PRServiceReq_SL( slave ); break; //resume into PR's own language env seanhalle@268: case Hardware: //for future expansion seanhalle@268: case IO: //for future expansion seanhalle@268: case OSCall: //for future expansion seanhalle@268: PR_int__throw_exception("Not implemented", slave, NULL); break; seanhalle@268: case Language: //normal lang request seanhalle@268: { seanhalle@268: (*protoLangEnv->requestHdlr)( req->langReq, slave, (void*)PR_int__give_lang_env(protoLangEnv )); seanhalle@268: } seanhalle@268: } seanhalle@268: seanhalle@268: MEAS__endReqHdlr; seanhalle@268: HOLISTIC__Record_AppResponder_end; seanhalle@268: } //if have request to be handled seanhalle@268: seanhalle@268: //If slot empty, hand to Assigner to fill with a slave seanhalle@268: if( slot->needsWorkAssigned ) seanhalle@268: { //Call plugin's Assigner to give slot a new slave seanhalle@268: HOLISTIC__Record_Assigner_start; seanhalle@268: seanhalle@268: if( protoLangEnv->hasWork ) seanhalle@268: { (*protoLangEnv->slaveAssigner)( protoLangEnv, slot ); //calls PR fn that inserts work into slot seanhalle@268: goto ReturnAfterAssigningWork; //quit for-loop, cause found work seanhalle@268: } seanhalle@268: else seanhalle@268: goto NoWork; seanhalle@268: } seanhalle@260: seanhalle@268: NoWork: seanhalle@268: //No work, if reach here.. seanhalle@268: { seanhalle@268: #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC seanhalle@268: coreNum = slot->coreSlotIsOn; seanhalle@268: returnSlv = process->idleSlv[coreNum][slotNum]; seanhalle@268: seanhalle@268: //things that would normally happen in resume(), but idle VPs seanhalle@268: // never go there seanhalle@268: returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID seanhalle@268: Unit newU; seanhalle@268: newU.vp = returnSlv->slaveNum; seanhalle@268: newU.task = returnSlv->numTimesAssignedToASlot; seanhalle@268: addToListOfArrays(Unit,newU,process->unitList); seanhalle@260: seanhalle@268: if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit seanhalle@268: { Dependency newD; // to this one seanhalle@268: newD.from_vp = returnSlv->slaveNum; seanhalle@268: newD.from_task = returnSlv->numTimesAssignedToASlot - 1; seanhalle@268: newD.to_vp = returnSlv->slaveNum; seanhalle@268: newD.to_task = returnSlv->numTimesAssignedToASlot; seanhalle@268: addToListOfArrays(Dependency, newD ,process->ctlDependenciesList); seanhalle@268: } seanhalle@268: #endif seanhalle@268: HOLISTIC__Record_Assigner_end; seanhalle@268: return; seanhalle@268: } seanhalle@268: seanhalle@268: ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic.. seanhalle@260: { seanhalle@268: HOLISTIC__Record_Assigner_end; seanhalle@268: return; seanhalle@267: } seanhalle@267: } seanhalle@268: */ seanhalle@267: seanhalle@261: inline seanhalle@261: void seanhalle@268: masterFunction_MultiLang( AnimSlot *slot ) seanhalle@261: { //Scan the animation slots seanhalle@261: int32 magicNumber; seanhalle@261: SlaveVP *slave; seanhalle@268: PRLangEnv *langEnv; seanhalle@261: PRReqst *req; seanhalle@261: RequestHandler requestHandler; seanhalle@268: PRProcess *process; seanhalle@260: seanhalle@268: //Check if newly-done slave in slot, which will need request handled seanhalle@268: if( slot->workIsDone ) seanhalle@268: { slot->workIsDone = FALSE; seanhalle@268: slot->needsWorkAssigned = TRUE; seanhalle@261: seanhalle@268: HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot seanhalle@268: MEAS__startReqHdlr; seanhalle@261: seanhalle@268: seanhalle@268: //process the request made by the slave (held inside slave struc) seanhalle@268: slave = slot->slaveAssignedToSlot; seanhalle@268: req = slave->request; seanhalle@268: seanhalle@268: //If the requesting slave is a slot slave, and request is not seanhalle@268: // task-end, then turn it into a free task slave. seanhalle@268: if( slave->typeOfVP == SlotTaskSlv && req->reqType != TaskEnd ) seanhalle@268: PR_int__replace_with_new_slot_slv( slave ); seanhalle@268: seanhalle@268: //Handle task create and end first -- they're special cases.. seanhalle@268: switch( req->reqType ) seanhalle@268: { case TaskEnd: seanhalle@268: { //do PR handler, which calls lang's hdlr and does recycle of seanhalle@268: // free task slave if needed -- PR handler checks for free task Slv seanhalle@268: PRHandle_EndTask( req, slave ); break; seanhalle@267: } seanhalle@268: case TaskCreate: seanhalle@268: { //Do PR's create-task handler, which calls the lang's hdlr seanhalle@268: // PR handler checks for free task Slv seanhalle@268: PRHandle_CreateTask( req, slave ); break; seanhalle@268: } seanhalle@268: case SlvCreate: PRHandle_CreateSlave( req, slave ); break; seanhalle@268: case SlvDissipate: PRHandle_Dissipate( req, slave ); break; seanhalle@268: case Service: PR_int__handle_PRServiceReq( slave ); break; //resume into PR's own language env seanhalle@268: case Hardware: //for future expansion seanhalle@268: case IO: //for future expansion seanhalle@268: case OSCall: //for future expansion seanhalle@268: PR_int__throw_exception("Not implemented", slave, NULL); break; seanhalle@268: case Language: //normal lang request seanhalle@268: { magicNumber = req->langMagicNumber; seanhalle@268: langEnv = PR_PI__give_lang_env_for( slave, magicNumber ); seanhalle@268: (*req->handler)( req->langReq, slave, langEnv ); seanhalle@268: } seanhalle@268: } seanhalle@261: seanhalle@268: MEAS__endReqHdlr; seanhalle@267: HOLISTIC__Record_AppResponder_end; seanhalle@268: } //if have request to be handled seanhalle@268: seanhalle@268: if( slot->needsWorkAssigned ) seanhalle@268: { seanhalle@268: HOLISTIC__Record_Assigner_start; seanhalle@268: seanhalle@268: //Pick a process to get this slot seanhalle@268: process = pickAProcess( slot ); seanhalle@268: seanhalle@268: //Scan lang environs, looking for langEnv with ready work. seanhalle@268: // call the Assigner for that lang Env, to get a slave for the slot seanhalle@268: assignWork( process, slot ); seanhalle@268: seanhalle@268: HOLISTIC__Record_Assigner_end; seanhalle@268: }//if slot needs slave assigned seanhalle@261: } seanhalle@260: seanhalle@268: /*When several processes exist, use some pattern for picking one to give seanhalle@268: * the animation slot to. seanhalle@268: *First, it has to be a process that has work available. seanhalle@268: *For now, just do a round-robin seanhalle@261: */ seanhalle@268: inline seanhalle@268: PRProcess * seanhalle@268: pickAProcess( AnimSlot *slot ) seanhalle@268: { int32 idx; seanhalle@268: PRProcess *process; seanhalle@268: seanhalle@268: for( idx = _PRTopEnv->currProcessIdx; idx < _PRTopEnv->numProcesses; idx++) seanhalle@268: { seanhalle@268: process = _PRTopEnv->processes[ idx ]; seanhalle@268: if( process->numEnvsWithWork != 0 ) seanhalle@268: { _PRTopEnv->currProcessIdx = idx; seanhalle@268: return process; seanhalle@268: } seanhalle@261: } seanhalle@268: for( idx = 0; idx < _PRTopEnv->currProcessIdx; idx++) seanhalle@268: { seanhalle@268: process = _PRTopEnv->processes[ idx ]; seanhalle@268: if( process->numEnvsWithWork != 0 ) seanhalle@268: { _PRTopEnv->currProcessIdx = idx; seanhalle@268: return process; seanhalle@268: } seanhalle@268: } seanhalle@268: //none found seanhalle@268: return NULL; seanhalle@260: } seanhalle@260: seanhalle@261: /*This does: seanhalle@268: * 1) searches the language environments for one with work ready seanhalle@261: * if finds one, asks its assigner to return work seanhalle@261: * 2) checks what kind of work: new task, resuming task, resuming slave seanhalle@261: * if new task, gets the slot slave and assigns task to it and returns slave seanhalle@261: * else, gets the slave attached to the metaTask and returns that. seanhalle@261: * 3) if no work found, then prune former task slaves waiting to be recycled. seanhalle@261: * If no work and no slaves to prune, check for shutdown conditions. seanhalle@261: * seanhalle@268: * language env keeps its own work in its own structures, and has its own seanhalle@261: * assigner. It chooses seanhalle@261: * However, include a switch that switches-in an override assigner, which seanhalle@268: * sees all the work in all the language env's. This is most likely seanhalle@261: * generated by static tools and included in the executable. That means it seanhalle@261: * has to be called via a registered pointer from here. The idea is that seanhalle@261: * the static tools know which languages are grouped together.. and the seanhalle@261: * override enables them to generate a custom assigner that uses info from seanhalle@261: * all the languages in a unified way.. Don't really expect this to happen, seanhalle@261: * but am making it possible. seanhalle@260: */ seanhalle@268: inline seanhalle@268: SlaveVP * seanhalle@261: assignWork( PRProcess *process, AnimSlot *slot ) seanhalle@261: { SlaveVP *returnSlv; seanhalle@261: int32 coreNum, slotNum; seanhalle@267: PRMetaTask *assignedMetaTask; seanhalle@260: seanhalle@261: coreNum = slot->coreSlotIsOn; seanhalle@260: seanhalle@267: if( process->overrideAssigner != NULL ) seanhalle@268: { if( process->numEnvsWithWork != 0 ) seanhalle@268: { (*process->overrideAssigner)( process, slot ); //calls PR fn that inserts work into slot seanhalle@268: goto ReturnAfterAssigningWork; //quit for-loop, cause found work seanhalle@261: } seanhalle@268: else seanhalle@261: goto NoWork; seanhalle@261: } seanhalle@261: seanhalle@268: //If here, then no override assigner, so search language envs for work seanhalle@268: int32 envIdx, numEnvs; PRLangEnv **langEnvsList, *langEnv; seanhalle@268: langEnvsList = process->langEnvsList; seanhalle@268: numEnvs = process->numLangEnvs; seanhalle@268: for( envIdx = 0; envIdx < numEnvs; envIdx++ ) //keep langEnvs in hash & array seanhalle@268: { langEnv = langEnvsList[envIdx]; seanhalle@268: if( langEnv->hasWork ) seanhalle@268: { (*langEnv->slaveAssigner)( langEnv, slot ); //assigner calls PR to put slave/task into slot seanhalle@268: goto ReturnAfterAssigningWork; //quit for-loop, cause found work seanhalle@268: //NOTE: bad search alg -- should start where left off, then wrap around seanhalle@260: } seanhalle@260: } seanhalle@268: //If reach here, then have searched all langEnv's & none have work.. seanhalle@260: seanhalle@268: NoWork: //No work, if end up here.. seanhalle@260: { seanhalle@260: #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC seanhalle@268: returnSlv = process->idleSlv[coreNum][slotNum]; seanhalle@260: seanhalle@260: //things that would normally happen in resume(), but idle VPs seanhalle@260: // never go there seanhalle@261: returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID seanhalle@260: Unit newU; seanhalle@268: newU.vp = returnSlv->slaveNum; seanhalle@261: newU.task = returnSlv->numTimesAssignedToASlot; seanhalle@261: addToListOfArrays(Unit,newU,process->unitList); seanhalle@260: seanhalle@261: if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit seanhalle@260: { Dependency newD; // to this one seanhalle@268: newD.from_vp = returnSlv->slaveNum; seanhalle@261: newD.from_task = returnSlv->numTimesAssignedToASlot - 1; seanhalle@268: newD.to_vp = returnSlv->slaveNum; seanhalle@261: newD.to_task = returnSlv->numTimesAssignedToASlot; seanhalle@261: addToListOfArrays(Dependency, newD ,process->ctlDependenciesList); seanhalle@260: } seanhalle@268: #endif seanhalle@268: HOLISTIC__Record_Assigner_end; seanhalle@268: return; seanhalle@260: } seanhalle@268: seanhalle@268: ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic.. seanhalle@268: { seanhalle@268: HOLISTIC__Record_Assigner_end; seanhalle@268: return; seanhalle@260: } seanhalle@260: } seanhalle@260: seanhalle@260: seanhalle@268: seanhalle@268: /*This is first thing called when creating a slave.. it hands off to the seanhalle@268: * langlet's creator, then adds updates of its own.. seanhalle@268: * seanhalle@268: *There's a question of things like lang data, meta tasks, and such.. seanhalle@268: *In creator, only PR related things happen, and things for the langlet whose seanhalle@261: * creator construct was used. seanhalle@268: * seanhalle@268: *Other langlets still get a chance to create langData -- but by registering a seanhalle@268: * "createLangData" handler in the langEnv. When a construct of the langlet seanhalle@268: * calls "PR__give_lang_data()", if there is no langData for that langlet, seanhalle@268: * the PR will call the creator in the langlet's langEnv, place whatever it seanhalle@268: * makes as the langData in that slave for that langlet, and return that langData seanhalle@261: * seanhalle@261: *So, as far as counting things, a langlet is only allowed to count creation seanhalle@261: * of slaves it creates itself.. may have to change this later.. add a way for seanhalle@261: * langlet to register a trigger Fn called each time a slave gets created.. seanhalle@261: * need more experience with what langlets will do at create time.. think Cilk seanhalle@261: * has interesting create behavior.. not sure how that will differ in light seanhalle@261: * of true tasks and langlet approach. Look at it after all done and start seanhalle@261: * modifying the langs to be langlets.. seanhalle@261: * seanhalle@261: *PR itself needs to create the slave, then update numLiveSlaves in process, seanhalle@261: * copy processID from requestor to newly created seanhalle@261: */ seanhalle@268: inline seanhalle@268: void seanhalle@268: PRHandle_CreateSlave( PRReqst *req, SlaveVP *slave ) seanhalle@268: { SlaveVP *newSlv; seanhalle@261: PRProcess *process; seanhalle@268: PRLangEnv *protoLangEnv; seanhalle@261: seanhalle@268: process = slave->processSlaveIsIn; seanhalle@268: protoLangEnv = PR_int__give_proto_lang_env_for_slave__ML( slave, req->langMagicNumber ); seanhalle@268: seanhalle@268: // newSlv = PR_int__create_slave( req->topLevelFn, req->initData ); seanhalle@268: seanhalle@268: //create slv has diff prototype than standard reqst hdlr seanhalle@268: newSlv = seanhalle@268: (*req->createHdlr)(req->langReq, slave, PR_int__give_lang_env(protoLangEnv)); seanhalle@268: seanhalle@261: newSlv->typeOfVP = GenericSlv; seanhalle@261: newSlv->processSlaveIsIn = process; seanhalle@268: newSlv->ID = req->ID; seanhalle@266: process->numLiveGenericSlvs += 1; seanhalle@260: } seanhalle@260: seanhalle@268: /*The dissipate handler has to, update the number of slaves of the type, within seanhalle@261: * the process, and call the langlet handler linked into the request, seanhalle@261: * and after that returns, then call the PR function that frees the slave state seanhalle@261: * (or recycles the slave). seanhalle@261: * seanhalle@261: *The PR function that frees the slave state has to also free all of the seanhalle@268: * langData in the slave.. or else reset all of the langDatas.. by, say, marking seanhalle@268: * them, then in PR__give_langData( magicNum ) call the langlet registered seanhalle@268: * "resetLangData" Fn. seanhalle@261: */ seanhalle@268: inline seanhalle@268: void seanhalle@268: PRHandle_Dissipate( PRReqst *req, SlaveVP *slave ) seanhalle@261: { PRProcess *process; seanhalle@268: PRLangEnv *protoLangEnv; seanhalle@261: seanhalle@261: process = slave->processSlaveIsIn; seanhalle@261: seanhalle@261: //do the language's dissipate handler seanhalle@268: protoLangEnv = PR_int__give_proto_lang_env_for_slave__ML( slave, slave->request->langMagicNumber ); seanhalle@268: seanhalle@268: if(req->handler != NULL) seanhalle@268: (*req->handler)( req->langReq, slave, PR_int__give_lang_env(protoLangEnv) ); seanhalle@261: seanhalle@266: process->numLiveGenericSlvs -= 1; seanhalle@268: PR_int__recycle_slave__ML( slave ); seanhalle@267: seanhalle@261: //check End Of Process Condition seanhalle@261: if( process->numLiveTasks == 0 && seanhalle@266: process->numLiveGenericSlvs == 0 ) seanhalle@268: PR_SS__shutdown_process__ML( process ); seanhalle@261: } seanhalle@261: seanhalle@261: /*Create task is a special form, that has PR behavior in addition to plugin seanhalle@268: * behavior. Master calls this first, and it then calls the plugin's seanhalle@261: * create task handler. seanhalle@267: * seanhalle@267: *Note: the requesting slave must be either generic slave or free task slave seanhalle@261: */ seanhalle@268: inline seanhalle@268: void seanhalle@268: PRHandle_CreateTask( PRReqst *req, SlaveVP *slave ) seanhalle@267: { PRMetaTask *metaTask; seanhalle@267: PRProcess *process; seanhalle@268: PRLangEnv *protoLangEnv; seanhalle@268: void *task; seanhalle@267: seanhalle@268: process = slave->processSlaveIsIn; seanhalle@268: seanhalle@268: protoLangEnv = PR_int__give_proto_lang_env_for_slave__ML( slave, seanhalle@268: req->langMagicNumber ); seanhalle@268: seanhalle@268: //Do the langlet's create-task handler, which keeps the task seanhalle@268: // inside the langlet's lang env, but returns the langMetaTask seanhalle@268: // so PR can put stuff into the prolog seanhalle@268: task = seanhalle@268: (*req->createHdlr)(req->langReq, slave, PR_int__give_lang_env(protoLangEnv) ); seanhalle@268: metaTask = PR_int__give_prolog_of_task( task ); seanhalle@268: metaTask->ID = req->ID; //may be NULL seanhalle@267: metaTask->topLevelFn = req->topLevelFn; seanhalle@267: metaTask->initData = req->initData; seanhalle@261: seanhalle@261: process->numLiveTasks += 1; seanhalle@267: seanhalle@261: return; seanhalle@261: } seanhalle@261: seanhalle@261: /*When a task ends, are two scenarios: 1) task ran to completion, or 2) task seanhalle@261: * suspended at some point in its code. seanhalle@261: *For 1, just decr count of live tasks (and check for end condition) -- the seanhalle@261: * master loop will decide what goes into the slot freed up by this task end, seanhalle@261: * so, here, don't worry about assigning a new task to the slot slave. seanhalle@261: *For 2, the task's slot slave has been converted to a free task slave, which seanhalle@261: * now has nothing more to do, so send it to the recycle Q (which includes seanhalle@268: * freeing all the langData and meta task structs alloc'd for it). Then seanhalle@261: * decrement the live task count and check end condition. seanhalle@261: * seanhalle@261: *PR has to update count of live tasks, and check end of process condition. seanhalle@267: * The "main" can invoke constructs that wait for a process to end, so when seanhalle@267: * end detected, have to resume what's waiting.. seanhalle@267: *Thing is, that wait involves the main OS thread. That means seanhalle@261: * PR internals have to do OS thread signaling. Want to do that in the seanhalle@267: * core controller, which has the original stack of an OS thread. So the seanhalle@267: * end process handling happens in the core controller. seanhalle@261: * seanhalle@261: *So here, when detect process end, signal to the core controller, which will seanhalle@267: * then do the condition variable notify to the OS thread that's waiting. seanhalle@267: * seanhalle@267: *Note: slave may be either a slot slave or a free task slave. seanhalle@261: */ seanhalle@268: inline seanhalle@268: void seanhalle@268: PRHandle_EndTask( PRReqst *req, SlaveVP *requestingSlv ) seanhalle@268: { void *langEnv; seanhalle@261: PRProcess *process; seanhalle@268: void *langMetaTask; seanhalle@267: seanhalle@268: langEnv = PR_int__give_lang_env_of_req__ML( req, requestingSlv ); //magic num in req seanhalle@268: langMetaTask = PR_int__give_lang_meta_task_from_slave__ML( requestingSlv, req->langMagicNumber); seanhalle@261: seanhalle@267: //Do the langlet's request handler seanhalle@268: //Want to keep PR structs hidden from plugin, so extract langReq.. seanhalle@268: (*req->handler)( req->langReq, requestingSlv, langEnv ); seanhalle@267: seanhalle@267: //Now that the langlet's done with it, recycle the slave if it's a freeTaskSlv seanhalle@267: if( requestingSlv->typeOfVP == FreeTaskSlv ) seanhalle@268: PR_int__recycle_slave__ML( requestingSlv ); seanhalle@261: seanhalle@261: process->numLiveTasks -= 1; seanhalle@261: seanhalle@261: //check End Of Process Condition seanhalle@261: if( process->numLiveTasks == 0 && seanhalle@266: process->numLiveGenericSlvs == 0 ) seanhalle@268: { //Tell the core controller to do wakeup of any waiting OS thread seanhalle@268: PR_SS__shutdown_process__ML( process ); seanhalle@268: } seanhalle@261: } seanhalle@261: