comparison AnimationMaster.c @ 261:dafae55597ce

Getting closer -- added PRServ as built-in langlet (but still just copy) about to rework a lot of the Master code.. possibly eliminate core controller
author Sean Halle <seanhalle@yahoo.com>
date Tue, 23 Oct 2012 23:46:17 -0700
parents 999f2966a3e5
children a5fa1e087c7e
comparison
equal deleted inserted replaced
9:f93b7a95183a 10:dd554750655b
8 8
9 #include <stdio.h> 9 #include <stdio.h>
10 #include <stddef.h> 10 #include <stddef.h>
11 11
12 #include "PR.h" 12 #include "PR.h"
13 13 #include "VSs_impl/VSs.h"
14
15 inline void
16 replaceWithNewSlotSlv( SlaveVP *requestingSlv, PRProcessEnv *processEnv );
14 17
15 18
16 /*The animationMaster embodies most of the animator of the language. The 19 /*The animationMaster embodies most of the animator of the language. The
17 * animator is what emodies the behavior of language constructs. 20 * animator is what emodies the behavior of language constructs.
18 * As such, it is the animationMaster, in combination with the plugin 21 * As such, it is the animationMaster, in combination with the plugin
35 * 38 *
36 * 39 *
37 * 40 *
38 */ 41 */
39 42
40 43
41 //===================== The versions of the Animation Master ================= 44 //===================== The versions of the Animation Master =================
42 // 45 //
43 //============================================================================== 46 //==============================================================================
44 47
45 /* 1) This version is for a single language, that has only slaves, no tasks, 48 /* 1) This version is for a single language, that has only slaves, no tasks,
103 *Implementation Details: 106 *Implementation Details:
104 * 107 *
105 *There is a separate masterVP for each core, but a single semantic 108 *There is a separate masterVP for each core, but a single semantic
106 * environment shared by all cores. Each core also has its own scheduling 109 * environment shared by all cores. Each core also has its own scheduling
107 * slots, which are used to communicate slaves between animationMaster and 110 * slots, which are used to communicate slaves between animationMaster and
108 * coreController. There is only one global variable, _PRMasterEnv, which 111 * coreController. There is only one global variable, _PRTopEnv, which
109 * holds the semantic env and other things shared by the different 112 * holds the semantic env and other things shared by the different
110 * masterVPs. The request handler and Assigner are registered with 113 * masterVPs. The request handler and Assigner are registered with
111 * the animationMaster by the language's init function, and a pointer to 114 * the animationMaster by the language's init function, and a pointer to
112 * each is in the _PRMasterEnv. (There are also some pthread related global 115 * each is in the _PRTopEnv. (There are also some pthread related global
113 * vars, but they're only used during init of PR). 116 * vars, but they're only used during init of PR).
114 *PR gains control over the cores by essentially "turning off" the OS's 117 *PR gains control over the cores by essentially "turning off" the OS's
115 * scheduler, using pthread pin-to-core commands. 118 * scheduler, using pthread pin-to-core commands.
116 * 119 *
117 *The masterVPs are created during init, with this animationMaster as their 120 *The masterVPs are created during init, with this animationMaster as their
120 *A "seed slave" is also created during init -- this is equivalent to the 123 *A "seed slave" is also created during init -- this is equivalent to the
121 * "main" function in C, and acts as the entry-point to the PR-language- 124 * "main" function in C, and acts as the entry-point to the PR-language-
122 * based application. 125 * based application.
123 *The masterVPs share a single system-wide master-lock, so only one 126 *The masterVPs share a single system-wide master-lock, so only one
124 * masterVP may be animated at a time. 127 * masterVP may be animated at a time.
125 *The core controllers access _PRMasterEnv to get the masterVP, and when 128 *The core controllers access _PRTopEnv to get the masterVP, and when
126 * they start, the slots are all empty, so they run their associated core's 129 * they start, the slots are all empty, so they run their associated core's
127 * masterVP. The first of those to get the master lock sees the seed slave 130 * masterVP. The first of those to get the master lock sees the seed slave
128 * in the shared semantic environment, so when it runs the Assigner, that 131 * in the shared semantic environment, so when it runs the Assigner, that
129 * returns the seed slave, which the animationMaster puts into a scheduling 132 * returns the seed slave, which the animationMaster puts into a scheduling
130 * slot then switches to the core controller. That then switches the core 133 * slot then switches to the core controller. That then switches the core
158 RequestHandler requestHandler; 161 RequestHandler requestHandler;
159 void *semanticEnv; 162 void *semanticEnv;
160 int32 thisCoresIdx; 163 int32 thisCoresIdx;
161 164
162 //======================== Initializations ======================== 165 //======================== Initializations ========================
163 masterEnv = (MasterEnv*)_VMSMasterEnv; 166 masterEnv = (MasterEnv*)_PRTopEnv;
164 167
165 thisCoresIdx = masterVP->coreAnimatedBy; 168 thisCoresIdx = masterVP->coreAnimatedBy;
166 animSlots = masterEnv->allAnimSlots[thisCoresIdx]; 169 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
167 170
168 requestHandler = masterEnv->requestHandler; 171 requestHandler = masterEnv->requestHandler;
194 currSlot->workIsDone = FALSE; 197 currSlot->workIsDone = FALSE;
195 currSlot->needsSlaveAssigned = TRUE; 198 currSlot->needsSlaveAssigned = TRUE;
196 SlaveVP *currSlave = currSlot->slaveAssignedToSlot; 199 SlaveVP *currSlave = currSlot->slaveAssignedToSlot;
197 200
198 justAddedReqHdlrChg(); 201 justAddedReqHdlrChg();
199 //handle the request, either by VMS or by the language 202 //handle the request, either by PR or by the language
200 if( currSlave->requests->reqType != LangReq ) 203 if( currSlave->requests->reqType != LangReq )
201 { //The request is a standard VMS one, not one defined by the 204 { //The request is a standard PR one, not one defined by the
202 // language, so VMS handles it, then queues slave to be assigned 205 // language, so PR handles it, then queues slave to be assigned
203 handleReqInVMS( currSlave ); 206 handleReqInPR( currSlave );
204 writePrivQ( currSlave, VMSReadyQ ); //Q slave to be assigned below 207 writePrivQ( currSlave, PRReadyQ ); //Q slave to be assigned below
205 } 208 }
206 else 209 else
207 { MEAS__startReqHdlr; 210 { MEAS__startReqHdlr;
208 211
209 //Language handles request, which is held inside slave struc 212 //Language handles request, which is held inside slave struc
270 PRConstrEnvHolder *constrEnvHolder; 273 PRConstrEnvHolder *constrEnvHolder;
271 int32 langMagicNumber; 274 int32 langMagicNumber;
272 //#endif 275 //#endif
273 276
274 //======================== Initializations ======================== 277 //======================== Initializations ========================
275 masterEnv = (MasterEnv*)_PRMasterEnv; 278 masterEnv = (MasterEnv*)_PRTopEnv;
276 279
277 thisCoresIdx = masterVP->coreAnimatedBy; 280 thisCoresIdx = masterVP->coreAnimatedBy;
278 animSlots = masterEnv->allAnimSlots[thisCoresIdx]; 281 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
279 282
280 requestHandler = masterEnv->requestHandler; 283 requestHandler = masterEnv->requestHandler;
496 PRConstrEnvHolder *constrEnvHolder; 499 PRConstrEnvHolder *constrEnvHolder;
497 int32 langMagicNumber; 500 int32 langMagicNumber;
498 //#endif 501 //#endif
499 502
500 //======================== Initializations ======================== 503 //======================== Initializations ========================
501 masterEnv = (MasterEnv*)_PRMasterEnv; 504 masterEnv = (MasterEnv*)_PRTopEnv;
502 505
503 thisCoresIdx = masterVP->coreAnimatedBy; 506 thisCoresIdx = masterVP->coreAnimatedBy;
504 animSlots = masterEnv->allAnimSlots[thisCoresIdx]; 507 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
505 508
506 requestHandler = masterEnv->requestHandler; 509 requestHandler = masterEnv->requestHandler;
612 //This is the master when both multi-lang and multi-process modes are turned on 615 //This is the master when both multi-lang and multi-process modes are turned on
613 //#ifdef MODE__MULTI_LANG 616 //#ifdef MODE__MULTI_LANG
614 //#ifdef MODE__MULTI_PROCESS 617 //#ifdef MODE__MULTI_PROCESS
615 void animationMaster( void *initData, SlaveVP *masterVP ) 618 void animationMaster( void *initData, SlaveVP *masterVP )
616 { 619 {
620 int32 slotIdx;
621 // int32 numSlotsFilled;
622 AnimSlot *currSlot;
617 //Used while scanning and filling animation slots 623 //Used while scanning and filling animation slots
618 int32 slotIdx, numSlotsFilled; 624 AnimSlot **animSlots;
619 AnimSlot *currSlot, **animSlots;
620 SlaveVP *assignedSlaveVP; //the slave chosen by the assigner
621 625
622 //Local copies, for performance 626 //Local copies, for performance
623 MasterEnv *masterEnv; 627 MasterEnv *masterEnv;
624 SlaveAssigner slaveAssigner;
625 RequestHandler requestHandler;
626 PRSemEnv *semanticEnv;
627 int32 thisCoresIdx; 628 int32 thisCoresIdx;
628
629 SlaveVP *slave;
630 PRProcess *process;
631 PRConstrEnvHolder *constrEnvHolder;
632 int32 langMagicNumber;
633 629
634 //======================== Initializations ======================== 630 //======================== Initializations ========================
635 masterEnv = (MasterEnv*)_PRMasterEnv; 631 masterEnv = (MasterEnv*)_PRTopEnv;
636 632
637 thisCoresIdx = masterVP->coreAnimatedBy; 633 thisCoresIdx = masterVP->coreAnimatedBy;
638 animSlots = masterEnv->allAnimSlots[thisCoresIdx]; 634 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
639 635
640 requestHandler = masterEnv->requestHandler;
641 slaveAssigner = masterEnv->slaveAssigner;
642 semanticEnv = masterEnv->semanticEnv;
643
644 //initialize, for non-multi-lang, non multi-proc case
645 // default handler gets put into master env by a registration call by lang
646 endTaskHandler = masterEnv->defaultTaskHandler;
647
648 HOLISTIC__Insert_Master_Global_Vars; 636 HOLISTIC__Insert_Master_Global_Vars;
649 637
650 //======================== animationMaster ======================== 638 //======================== animationMaster ========================
651 //Do loop gets requests handled and work assigned to slots.. 639 //Do loop gets requests handled and work assigned to slots..
652 // work can either be a task or a resumed slave 640 // work can either be a task or a resumed slave
653 //Having two cases makes this logic complex.. can be finishing either, and 641 //Having two cases makes this logic complex.. can be finishing either, and
654 // then the next available work may be either.. so really have two distinct 642 // then the next available work may be either.. so really have two distinct
655 // loops that are inter-twined.. 643 // loops that are inter-twined..
656 while(1){ 644 while(1)
657 645 {
658 MEAS__Capture_Pre_Master_Point 646 MEAS__Capture_Pre_Master_Point
659 647
660 //Scan the animation slots 648 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
661 numSlotsFilled = 0; 649 {
662 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++) 650 currSlot = animSlots[ slotIdx ];
663 { 651
664 currSlot = animSlots[ slotIdx ]; 652 masterFunction_multiLang( currSlot );
653 }
654
655 MEAS__Capture_Post_Master_Point;
656
657 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
658 flushRegisters();
659 }
660 }
661 #endif //MODE__MULTI_LANG
662 #endif //MODE__MULTI_PROCESS
663
664 inline
665 void
666 masterFunction_multiLang( AnimSlot *currSlot )
667 { //Scan the animation slots
668 int32 magicNumber;
669 SlaveVP *slave;
670 SlaveVP *assignedSlaveVP;
671 PRSemEnv *semanticEnv;
672 PRReqst *req;
673 RequestHandler requestHandler;
665 674
666 //Check if newly-done slave in slot, which will need request handled 675 //Check if newly-done slave in slot, which will need request handled
667 if( currSlot->workIsDone ) 676 if( currSlot->workIsDone )
668 { currSlot->workIsDone = FALSE; 677 { currSlot->workIsDone = FALSE;
669 678
672 681
673 682
674 //process the request made by the slave (held inside slave struc) 683 //process the request made by the slave (held inside slave struc)
675 slave = currSlot->slaveAssignedToSlot; 684 slave = currSlot->slaveAssignedToSlot;
676 685
677 //check if the completed work was a task.. 686 //check if the slave was doing a task..
678 if( slave->taskMetaInfo->isATask ) 687 //Action depends on both on the request type, and whether it's on
679 { 688 // a generic slave vs a suspended task
680 if( slave->reqst->type == TaskEnd ) 689 if( slave->metaTask->taskType == AtomicTask ||
681 { //do task end handler, which is registered separately 690 slave->metaTask->taskType == SuspendedTask )
682 //note, end hdlr may use semantic data from reqst.. 691 {
683 //get end-task handler 692 switch( slave->request->reqType )
684 //taskEndHandler = lookup( slave->reqst->langMagicNumber, processEnv ); 693 { case TaskEnd:
685 taskEndHandler = slave->taskMetaInfo->endTaskHandler; 694 { PRHandle_EndTask( slave ); //if free task slave, update count, put into recycle Q -- do handler before lang's handler
686 695
687 (*taskEndHandler)( slave, semanticEnv ); 696 //do task end handler, which is registered separately
688 697 //note, end hdlr may use semantic data from reqst..
689 goto AssignWork; 698 //get end-task handler
690 } 699
691 else //is a task, and just suspended 700 RequestHandler
692 { //turn slot slave into free task slave & make replacement 701 taskEndHandler = slave->metaTask->reqHandler;
693 if( slave->typeOfVP == TaskSlotSlv ) changeSlvType(); 702 semanticEnv = PR_int__give_sem_env_for_slave( slave,
694 703 slave->request->langMagicNumber );
695 //goto normal slave request handling 704 (*taskEndHandler)( slave, semanticEnv );
696 goto SlaveReqHandling; 705
697 } 706 goto AssignWork;
707 }
708 case TaskCreate:
709 { PRHandle_CreateTask( slave );
710 justCopied_check;
711 RequestHandler
712 taskCreateHandler = slave->metaTask->reqHandler;
713 semanticEnv = PR_int__give_sem_env_for_slave( slave,
714 slave->request->langMagicNumber );
715 (*taskCreateHandler)( slave, semanticEnv );
716
717 want_to_resume_creating_slave;
718 goto AssignWork;
719 }
720 default:
721 { //is a task, and just suspended, so tied to a free task slave
722 //First turn slot slave into free task slave & make replacement
723 if( slave->typeOfVP == TaskSlotSlv )
724 replaceWithNewSlotSlv( slave, slave->processSlaveIsIn->processEnv );
725
726 //goto normal slave request handling
727 goto SlaveReqHandling;
728 }
729 }
698 } 730 }
699 else //is a slave that suspended 731 else //is a slave that suspended
700 { 732 {
701 733
702 SlaveReqHandling: 734 SlaveReqHandling:
703 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave) 735 //Q: put the switch in inline call, to clean up code?
704 736 req = slave->request;
737 switch( req->reqType )
738 { case SlvCreate: PRHandle_CreateSlave( slave ); break;
739 case SlvDissipate: PRHandle_Dissipate( slave ); break;
740 case Service: PR_int__handle_PRServiceReq( slave ); break; //resume into PR's own semantic env
741 case Hardware: //for future expansion
742 case IO: //for future expansion
743 case OSCall: //for future expansion
744 case Language: //normal sem request
745 magicNumber = slave->request->langMagicNumber;
746 semanticEnv = PR_PI__give_sem_env_for( slave, magicNumber );
747 requestHandler = semanticEnv->requestHdlr;
748 (*requestHandler)( slave, semanticEnv ); //(note: indirect Fn call more efficient when use fewer params, instead re-fetch from slave)
749 }
750
705 HOLISTIC__Record_AppResponder_end; 751 HOLISTIC__Record_AppResponder_end;
706 MEAS__endReqHdlr; 752 MEAS__endReqHdlr;
707 753
708 goto AssignWork; 754 goto AssignWork;
709 } 755 }
710 } //if has suspended slave that needs handling 756 } //if has suspended slave that needs handling
711 757
712 //if slot empty, hand to Assigner to fill with a slave 758 //End up here when the slot did not have ended work in it (no req)
759 //So, here, if slot empty, look for work to fill the slot
713 if( currSlot->needsSlaveAssigned ) 760 if( currSlot->needsSlaveAssigned )
714 { //Scan sem environs, looking for one with ready work. 761 { HOLISTIC__Record_Assigner_start;
715 // call the Assigner for that sem Env, to give slot a new slave
716 HOLISTIC__Record_Assigner_start;
717 762
718 AssignWork: 763 AssignWork:
719 764 //Scan sem environs, looking for semEnv with ready work.
765 // call the Assigner for that sem Env, to get a slave for the slot
720 assignedSlaveVP = assignWork( semanticEnv, currSlot ); 766 assignedSlaveVP = assignWork( semanticEnv, currSlot );
721 767
722 //put the chosen slave into slot, and adjust flags and state 768 //put the chosen slave into slot, and adjust flags and state
723 if( assignedSlaveVP != NULL ) 769 if( assignedSlaveVP != NULL )
724 { currSlot->slaveAssignedToSlot = assignedSlaveVP; 770 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
725 assignedSlaveVP->animSlotAssignedTo = currSlot; 771 assignedSlaveVP->animSlotAssignedTo = currSlot;
726 currSlot->needsSlaveAssigned = FALSE; 772 currSlot->needsSlaveAssigned = FALSE;
727 numSlotsFilled += 1;
728 } 773 }
729 else 774 else
730 { 775 { currSlot->needsSlaveAssigned = TRUE; //local write
731 currSlot->needsSlaveAssigned = TRUE; //local write
732 } 776 }
733 HOLISTIC__Record_Assigner_end; 777 HOLISTIC__Record_Assigner_end;
734 }//if slot needs slave assigned 778 }//if slot needs slave assigned
735 }//for( slotIdx..
736
737 MEAS__Capture_Post_Master_Point;
738
739 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
740 flushRegisters();
741 }//while(1)
742 } 779 }
743 #endif //MODE__MULTI_LANG 780
744 #endif //MODE__MULTI_PROCESS 781 //==========================================================================
745 782 /*When a task in a slot slave suspends, the slot slave has to be changed to
746 783 * a free task slave, then the slot slave replaced. The replacement can be
747 /*This does three things: 784 * either a recycled free task slave that finished it's task and has been
748 * 1) ask for a slave ready to resume 785 * idle in the recycle queue, or else create a new slave to be the slot slave.
749 * 2) if none, then ask for a task, and assign to the slot slave 786 *The master only calls this with a slot slave that needs to be replaced.
750 * 3) if none, then prune former task slaves waiting to be recycled. 787 */
751 * 788 inline void
752 //Have two separate assigners in each semantic env, 789 replaceWithNewSlotSlv( SlaveVP *requestingSlv, PRProcessEnv *processEnv )
753 // which keeps its own work in its own structures.. the master, here, 790 { SlaveVP *newSlotSlv;
754 // searches through the semantic environs, takes the first that has work 791 VSsSemData *semData;
755 // available, and whatever it returns is assigned to the slot.. 792
756 //However, also have an override assigner.. because static analysis tools know 793 fixMe__still_VSs_stuff_in_here;
757 // which languages are grouped together.. and the override enables them to 794 //get a new slave to be the slot slave
758 // generate a custom assigner that uses info from all the languages in a 795 newSlotSlv = readPrivQ( processEnv->freeTaskSlvRecycleQ );
759 // unified way.. Don't really expect this to happen, but making it possible. 796 if( newSlotSlv == NULL )
797 { newSlotSlv = PR_int__create_slaveVP( &idle_fn, NULL, processEnv, 0);
798 //just made a new free task slave, so count it
799 processEnv->numLiveFreeTaskSlvs += 1;
800 }
801
802 //set slave values to make it the slot slave
803 newSlotSlv->metaTask = NULL;
804 newSlotSlv->typeOfVP = TaskSlotSlv;
805 newSlotSlv->needsTaskAssigned = TRUE;
806
807 //a slot slave is pinned to a particular slot on a particular core
808 //Note, this happens before the request is seen by handler, so nothing
809 // has had a chance to change the coreAnimatedBy or anything else..
810 newSlotSlv->animSlotAssignedTo = requestingSlv->animSlotAssignedTo;
811 newSlotSlv->coreAnimatedBy = requestingSlv->coreAnimatedBy;
812
813 //put it into the slot slave matrix
814 int32 slotNum = requestingSlv->animSlotAssignedTo->slotIdx;
815 int32 coreNum = requestingSlv->coreAnimatedBy;
816 processEnv->slotTaskSlvs[coreNum][slotNum] = newSlotSlv;
817
818 //Fix up requester, to be an extra slave now (but not an ended one)
819 // because it's active, doesn't go into freeTaskSlvRecycleQ
820 requestingSlv->typeOfVP = FreeTaskSlv;
821 }
822
823
824
825 /*This does:
826 * 1) searches the semantic environments for one with work ready
827 * if finds one, asks its assigner to return work
828 * 2) checks what kind of work: new task, resuming task, resuming slave
829 * if new task, gets the slot slave and assigns task to it and returns slave
830 * else, gets the slave attached to the metaTask and returns that.
831 * 3) if no work found, then prune former task slaves waiting to be recycled.
832 * If no work and no slaves to prune, check for shutdown conditions.
833 *
834 * Semantic env keeps its own work in its own structures, and has its own
835 * assigner. It chooses
836 * However, include a switch that switches-in an override assigner, which
837 * sees all the work in all the semantic env's. This is most likely
838 * generated by static tools and included in the executable. That means it
839 * has to be called via a registered pointer from here. The idea is that
840 * the static tools know which languages are grouped together.. and the
841 * override enables them to generate a custom assigner that uses info from
842 * all the languages in a unified way.. Don't really expect this to happen,
843 * but am making it possible.
760 */ 844 */
761 inline SlaveVP * 845 inline SlaveVP *
762 assignWork( PRProcessEnv *processEnv, AnimSlot *slot ) 846 assignWork( PRProcess *process, AnimSlot *slot )
763 { SlaveVP *returnSlv; 847 { SlaveVP *returnSlv;
764 //VSsSemEnv *semEnv; 848 //VSsSemEnv *semEnv;
765 //VSsSemData *semData; 849 //VSsSemData *semData;
766 int32 coreNum, slotNum; 850 int32 coreNum, slotNum;
767 PRTaskMetaInfo *newTaskStub; 851 PRMetaTask *newMetaTask, *assignedMetaTask;
768 SlaveVP *freeTaskSlv; 852 SlaveVP *freeTaskSlv;
769 853
770 854 coreNum = slot->coreSlotIsOn;
771 //master has to handle slot slaves.. so either assigner returns 855
772 // taskMetaInfo or else two assigners, one for slaves, other for tasks.. 856 if( _PRTopEnv->overrideAssigner != NULL )
773 semEnvs = processEnv->semEnvs; 857 { assignedMetaTask = (*_PRTopEnv->overrideAssigner)( process, slot );
774 numEnvs = processEnv->numSemEnvs; 858 if( assignedMetaTask != NULL )
775 for( envIdx = 0; envIdx < numEnvs; envIdx++ ) 859 {
860 //have work, so reset Done flag (caused by work generated on other core)
861 if( process->coreIsDone[coreNum] == TRUE ) //reads are higher perf
862 process->coreIsDone[coreNum] = FALSE; //don't just write always
863
864 switch( assignedMetaTask->taskType )
865 { case GenericSlave: goto AssignSlave;
866 case ResumedTask: goto AssignSlave;
867 case NewTask: goto AssignNewTask;
868 case default: PR_int__throw_exception( "unknown task type ret by assigner" );
869 }
870 }
871 else
872 goto NoWork;
873 }
874
875 //If here, then no override assigner, so search semantic envs for work
876 int32 envIdx, numEnvs; PRSemEnv **semEnvs, *semEnv; SlaveAssigner assigner;
877 semEnvs = process->semEnvs;
878 numEnvs = process->numSemEnvs;
879 for( envIdx = 0; envIdx < numEnvs; envIdx++ ) //keep semEnvs in hash AND array
776 { semEnv = semEnvs[envIdx]; 880 { semEnv = semEnvs[envIdx];
777 if( semEnv->hasWork ) 881 if( semEnv->hasWork )
778 { assigner = semEnv->assigner; 882 { assigner = semEnv->assigner;
779 retTaskMetaInfo = (*assigner)( semEnv, slot ); 883 assignedMetaTask = (*assigner)( semEnv, slot );
780 884
781 return retTaskMetaInfo; //quit, have work 885 //have work, so reset Done flag (caused by work generated on other core)
886 if( process->coreIsDone[coreNum] == TRUE ) //reads are higher perf
887 process->coreIsDone[coreNum] = FALSE; //don't just write always
888
889 switch( assignedMetaTask->taskType )
890 { case GenericSlave: goto AssignSlave;
891 case ResumedTask: goto AssignSlave;
892 case NewTask: goto AssignNewTask;
893 case default: PR_int__throw_exception( "unknown task type ret by assigner" );
894 }
782 } 895 }
783 } 896 }
784 897
785 coreNum = slot->coreSlotIsOn; 898 NoWork:
786 slotNum = slot->slotIdx; 899 //No work, if reach here..
787 900 //no task, so prune the recycle pool of free task slaves
788 //first try to get a ready slave 901 freeTaskSlv = readPrivQ( process->freeTaskSlvRecycleQ );
789 returnSlv = getReadySlave(); 902 if( freeTaskSlv != NULL )
790 903 { //delete, so that bound the num extras, and deliver shutdown cond
791 if( returnSlv != NULL ) 904 deleteExtraneousFreeTaskSlv( freeTaskSlv, process );
792 { returnSlv->coreAnimatedBy = coreNum; 905 //then return NULL
793 906 returnSlv = NULL;
794 //have work, so reset Done flag (when work generated on other core) 907
795 if( processEnv->coreIsDone[coreNum] == TRUE ) //reads are higher perf 908 goto ReturnTheSlv;
796 processEnv->coreIsDone[coreNum] = FALSE; //don't just write always 909 }
910 else
911 { //candidate for shutdown.. all extras dissipated, and no tasks
912 // and no ready to resume slaves, so no way to generate
913 // more work (on this core -- other core might have work still)
914 if( process->numLiveFreeTaskSlvs == 0 &&
915 process->numLiveGenericSlvs == 0 )
916 { //This core sees no way to generate more tasks, so say it
917 if( process->coreIsDone[coreNum] == FALSE )
918 { process->numCoresDone += 1;
919 process->coreIsDone[coreNum] = TRUE;
920 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
921 process->shutdownInitiated = TRUE;
922
923 #else
924 if( process->numCoresDone == NUM_CORES )
925 { //means no cores have work, and none can generate more
926 process->shutdownInitiated = TRUE;
927 }
928 #endif
929 }
930 }
931 //check if shutdown has been initiated by this or other core
932 if( process->shutdownInitiated )
933 { returnSlv = PR_SS__create_shutdown_slave();
934 }
935 else
936 returnSlv = NULL;
937
938 goto ReturnTheSlv;
939 } //if( freeTaskSlv != NULL )
940
941
942 AssignSlave:
943 { //get slave pointed to by meta task.
944 returnSlv = assignedMetaTask->slaveAssignedTo;
945
946 returnSlv->coreAnimatedBy = coreNum;
797 947
798 goto ReturnTheSlv; 948 goto ReturnTheSlv;
799 } 949 }
800 950
801 //were no slaves, so try to get a ready task.. 951 AssignNewTask:
802 newTaskStub = getTaskStub();
803
804 if( newTaskStub != NULL )
805 { 952 {
806 //get the slot slave to assign the task to.. 953 //get the slot slave to assign the task to..
807 returnSlv = processEnv->slotTaskSlvs[coreNum][slotNum]; 954 coreNum = slot->coreSlotIsOn;
955 slotNum = slot->slotIdx;
956 returnSlv = process->slotTaskSlvs[coreNum][slotNum];
808 957
809 //point slave to task's function, and mark slave as having task 958 //point slave to task's function, and mark slave as having task
810 PR_int__reset_slaveVP_to_TopLvlFn( returnSlv, 959 PR_int__reset_slaveVP_to_TopLvlFn( returnSlv,
811 newTaskStub->taskType->fn, newTaskStub->args ); 960 assignedMetaTask->topLevelFn, assignedMetaTask->initData );
812 returnSlv->taskStub = newTaskStub; 961 returnSlv->metaTask = assignedMetaTask;
813 newTaskStub->slaveAssignedTo = returnSlv; 962 assignedMetaTask->slaveAssignedTo = returnSlv;
814 returnSlv->needsTaskAssigned = FALSE; //slot slave is a "Task" slave type 963 returnSlv->needsTaskAssigned = FALSE; //slot slave is a "Task" slave type
815 964
816 //have work, so reset Done flag, if was set 965 //have work, so reset Done flag, if was set
817 if( processEnv->coreIsDone[coreNum] == TRUE ) //reads are higher perf 966 if( process->coreIsDone[coreNum] == TRUE ) //reads are higher perf
818 processEnv->coreIsDone[coreNum] = FALSE; //don't just write always 967 process->coreIsDone[coreNum] = FALSE; //don't just write always
819 968
820 goto ReturnTheSlv; 969 goto ReturnTheSlv;
821 } 970 }
822 else
823 { //no task, so prune the recycle pool of free task slaves
824 freeTaskSlv = readPrivQ( processEnv->freeTaskSlvRecycleQ );
825 if( freeTaskSlv != NULL )
826 { //delete to bound the num extras, and deliver shutdown cond
827 handleDissipate( freeTaskSlv, processEnv );
828 //then return NULL
829 returnSlv = NULL;
830
831 goto ReturnTheSlv;
832 }
833 else
834 { //candidate for shutdown.. if all extras dissipated, and no tasks
835 // and no ready to resume slaves, then no way to generate
836 // more tasks (on this core -- other core might have task still)
837 if( processEnv->numLiveExtraTaskSlvs == 0 &&
838 processEnv->numLiveThreadSlvs == 0 )
839 { //This core sees no way to generate more tasks, so say it
840 if( processEnv->coreIsDone[coreNum] == FALSE )
841 { processEnv->numCoresDone += 1;
842 processEnv->coreIsDone[coreNum] = TRUE;
843 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
844 processEnv->shutdownInitiated = TRUE;
845
846 #else
847 if( processEnv->numCoresDone == NUM_CORES )
848 { //means no cores have work, and none can generate more
849 processEnv->shutdownInitiated = TRUE;
850 }
851 #endif
852 }
853 }
854 //check if shutdown has been initiated by this or other core
855 if(processEnv->shutdownInitiated)
856 { returnSlv = PR_SS__create_shutdown_slave();
857 }
858 else
859 returnSlv = NULL;
860
861 goto ReturnTheSlv; //don't need, but completes pattern
862 } //if( freeTaskSlv != NULL )
863 } //if( newTaskStub == NULL )
864 //outcome: 1)slave was just pointed to task, 2)no tasks, so slave NULL
865 971
866 972
867 ReturnTheSlv: //All paths goto here.. to provide single point for holistic.. 973 ReturnTheSlv: //All paths goto here.. to provide single point for holistic..
868 974
869 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC 975 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
870 if( returnSlv == NULL ) 976 if( returnSlv == NULL )
871 { returnSlv = processEnv->idleSlv[coreNum][slotNum]; 977 { returnSlv = process->idleSlv[coreNum][slotNum];
872 978
873 //things that would normally happen in resume(), but idle VPs 979 //things that would normally happen in resume(), but idle VPs
874 // never go there 980 // never go there
875 returnSlv->assignCount++; //gives each idle unit a unique ID 981 returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID
876 Unit newU; 982 Unit newU;
877 newU.vp = returnSlv->slaveID; 983 newU.vp = returnSlv->slaveID;
878 newU.task = returnSlv->assignCount; 984 newU.task = returnSlv->numTimesAssignedToASlot;
879 addToListOfArrays(Unit,newU,processEnv->unitList); 985 addToListOfArrays(Unit,newU,process->unitList);
880 986
881 if (returnSlv->assignCount > 1) //make a dependency from prev idle unit 987 if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit
882 { Dependency newD; // to this one 988 { Dependency newD; // to this one
883 newD.from_vp = returnSlv->slaveID; 989 newD.from_vp = returnSlv->slaveID;
884 newD.from_task = returnSlv->assignCount - 1; 990 newD.from_task = returnSlv->numTimesAssignedToASlot - 1;
885 newD.to_vp = returnSlv->slaveID; 991 newD.to_vp = returnSlv->slaveID;
886 newD.to_task = returnSlv->assignCount; 992 newD.to_task = returnSlv->numTimesAssignedToASlot;
887 addToListOfArrays(Dependency, newD ,processEnv->ctlDependenciesList); 993 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList);
888 } 994 }
889 } 995 }
890 else //have a slave will be assigned to the slot 996 else //have a slave will be assigned to the slot
891 { //assignSlv->numTimesAssigned++; 997 { //assignSlv->numTimesAssigned++;
892 //get previous occupant of the slot 998 //get previous occupant of the slot
893 Unit prev_in_slot = 999 Unit prev_in_slot =
894 processEnv->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum]; 1000 process->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum];
895 if(prev_in_slot.vp != 0) //if not first slave in slot, make dependency 1001 if(prev_in_slot.vp != 0) //if not first slave in slot, make dependency
896 { Dependency newD; // is a hardware dependency 1002 { Dependency newD; // is a hardware dependency
897 newD.from_vp = prev_in_slot.vp; 1003 newD.from_vp = prev_in_slot.vp;
898 newD.from_task = prev_in_slot.task; 1004 newD.from_task = prev_in_slot.task;
899 newD.to_vp = returnSlv->slaveID; 1005 newD.to_vp = returnSlv->slaveID;
900 newD.to_task = returnSlv->assignCount; 1006 newD.to_task = returnSlv->numTimesAssignedToASlot;
901 addToListOfArrays(Dependency,newD,processEnv->hwArcs); 1007 addToListOfArrays(Dependency,newD,process->hwArcs);
902 } 1008 }
903 prev_in_slot.vp = returnSlv->slaveID; //make new slave the new previous 1009 prev_in_slot.vp = returnSlv->slaveID; //make new slave the new previous
904 prev_in_slot.task = returnSlv->assignCount; 1010 prev_in_slot.task = returnSlv->numTimesAssignedToASlot;
905 processEnv->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum] = 1011 process->last_in_slot[coreNum * NUM_ANIM_SLOTS + slotNum] =
906 prev_in_slot; 1012 prev_in_slot;
907 } 1013 }
908 #endif 1014 #endif
909 1015
910 return( returnSlv ); 1016 return( returnSlv );
911 } 1017 }
912 1018
1019
1020 /*In creator, only PR related things happen, and things in the langlet whose
1021 * creator construct was used.
1022 *Other langlet still gets a chance to create semData -- but by registering a
1023 * "createSemData" handler in the semEnv. When a construct of the langlet
1024 * calls "PR__give_sem_data()", if there is no semData for that langlet,
1025 * the PR will call the creator in the langlet's semEnv, place whatever it
1026 * makes as the semData in that slave for that langlet, and return that semData
1027 *
1028 *So, as far as counting things, a langlet is only allowed to count creation
1029 * of slaves it creates itself.. may have to change this later.. add a way for
1030 * langlet to register a trigger Fn called each time a slave gets created..
1031 * need more experience with what langlets will do at create time.. think Cilk
1032 * has interesting create behavior.. not sure how that will differ in light
1033 * of true tasks and langlet approach. Look at it after all done and start
1034 * modifying the langs to be langlets..
1035 *
1036 *PR itself needs to create the slave, then update numLiveSlaves in process,
1037 * copy processID from requestor to newly created
1038 */
1039 PRHandle_CreateSlave( PRReqst *req, SlaveVP *requestingSlv )
1040 { SlaveVP *newSlv;
1041 PRMetaTask metaTask;
1042 PRProcess *process;
1043
1044 process = requestingSlv->processSlaveIsIn;
1045 newSlv = PR_int__create_slaveVP();
1046 newSlv->typeOfVP = GenericSlv;
1047 newSlv->processSlaveIsIn = process;
1048 process->numLiveGenericSlaves += 1;
1049 metaTask = PR_int__create_slave_meta_task();
1050 metaTask->taskID = req->ID;
1051 metaTask->taskType = GenericSlave;
1052
1053 (*req->handler)(newSlv);
1054 }
1055
1056 /*The dissipate handler has to update the number of slaves of the type, within
1057 * the process, and call the langlet handler linked into the request,
1058 * and after that returns, then call the PR function that frees the slave state
1059 * (or recycles the slave).
1060 *
1061 *The PR function that frees the slave state has to also free all of the
1062 * semData in the slave.. or else reset all of the semDatas.. by, say, marking
1063 * them, then in PR__give_semData( magicNum ) call the langlet registered
1064 * "resetSemData" Fn.
1065 */
1066 PRHandle_Dissipate( SlaveVP *slave )
1067 { PRProcess *process;
1068 void *semEnv;
1069
1070 process = slave->processSlaveIsIn;
1071
1072 //do the language's dissipate handler
1073 semEnv = PR_int__give_sem_env_for( slave, slave->request->langMagicNumber );
1074 (*slave->request->handler)( slave, semEnv );
1075
1076 process->numLiveGenericSlaves -= 1;
1077 PR_int__dissipate_slaveVP_multilang( slave ); //recycles and resets semDatas
1078
1079 //check End Of Process Condition
1080 if( process->numLiveTasks == 0 &&
1081 process->numLiveGenericSlaves == 0 )
1082 signalEndOfProcess;
1083 }
1084
1085 /*Create task is a special form, that has PR behavior in addition to plugin
1086 * behavior. Master calls this first, and this in turn calls the plugin's
1087 * create task handler.
1088 */
1089 inline void
1090 PRHandle_CreateTask( TopLevelFn topLevelFn, void *initData, PRReqst *req,
1091 SlaveVP *requestingSlv )
1092 { PRMetaTask *metaTask;
1093 PRProcess *process;
1094 void *semEnv, _langMetaTask;
1095 PRLangMetaTask *langMetaTask;
1096
1097 process = requestingSlv->processSlaveIsIn;
1098
1099 metaTask = PR_int__create_meta_task( req );
1100 metaTask->taskID = req->ID; //may be NULL
1101 metaTask->topLevelFn = topLevelFn;
1102 metaTask->initData = initData;
1103
1104 process->numLiveTasks += 1;
913 1105
914 //================================================================= 1106 //plugin tracks tasks ready, and has its own assigner, so task doesn't
915 //#else //is MODE__MULTI_LANG 1107 // come back from lang's handler -- it's consumed and stays in semEnv.
916 //For multi-lang mode, first, get the constraint-env holder out of 1108 //But handler gives back the language-specific meta-task it creates, and
917 // the process, which is in the slave. 1109 // then hook that into the PR meta-task
918 //Second, get the magic number out of the request, use it to look up 1110 //(Could also do PRMetaTask as a prolog -- make a Fn that takes the size
919 // the constraint Env within the constraint-env holder. 1111 // of the lang's metaTask, and alloc's that plus the prolog and returns
920 //Then get the request handler out of the constr env 1112 // ptr to position just above the prolog)
921 constrEnvHolder = slave->process->constrEnvHolder; 1113 semEnv = PR_int__give_semEnv_of_req( req, requestingSlv ); //magic num in req
922 reqst = slave->request; 1114 _langMetaTask = (*requestingSlv->request->handler)(req, semEnv);
923 langMagicNumber = reqst->langMagicNumber; 1115 langMetaTask = (PRLangMetaTask *)_langMetaTask;
924 semanticEnv = lookup( langMagicNumber, constrEnvHolder ); //a macro 1116 metaTask->langMetaTask = langMetaTask;
925 if( slave->reqst->type == taskEnd ) //end-task is special 1117 langMetaTask->protoMetaTask = metaTask;
926 { //need to know what lang's task ended 1118
927 taskEndHandler = semanticEnv->taskEndHandler; 1119 return;
928 (*taskEndHandler)( slave, reqst, semanticEnv ); //can put semantic data into task end reqst, for continuation, etc
929 //this is a slot slave, get a new task for it
930 if( !existsOverrideAssigner )//if exists, is set above, before loop
931 { //search for task assigner that has work
932 for( a = 0; a < num_assigners; a++ )
933 { if( taskAssigners[a]->hasWork )
934 { newTaskAssigner = taskAssigners[a];
935 (*newTaskAssigner)( slave, semanticEnv );
936 goto GotTask;
937 }
938 }
939 goto NoTasks;
940 }
941
942 GotTask:
943 continue; //have work, so do next iter of loop, don't call slave assigner
944 }
945 if( slave->typeOfVP == taskSlotSlv ) changeSlvType();//is suspended task
946 //now do normal suspended slave request handler
947 requestHandler = semanticEnv->requestHandler;
948 //#endif
949
950
951 }
952 //If make it here, then was no task for this slot
953 //slot empty, hand to Assigner to fill with a slave
954 if( currSlot->needsSlaveAssigned )
955 { //Call plugin's Assigner to give slot a new slave
956 HOLISTIC__Record_Assigner_start;
957
958 //#ifdef MODE__MULTI_LANG
959 NoTasks:
960 //First, choose an Assigner..
961 //There are several Assigners, one for each langlet.. they all
962 // indicate whether they have work available.. just pick the first
963 // one that has work.. Or, if there's a Unified Assigner, call
964 // that one.. So, go down array, checking..
965 if( !existsOverrideAssigner )
966 { for( a = 0; a < num_assigners; a++ )
967 { if( assigners[a]->hasWork )
968 { slaveAssigner = assigners[a];
969 goto GotAssigner;
970 }
971 }
972 //no work, so just continue to next iter of scan loop
973 continue;
974 }
975 //when exists override, the assigner is set, once, above, so do nothing
976 GotAssigner:
977 //#endif
978
979 assignedSlaveVP =
980 (*slaveAssigner)( semanticEnv, currSlot );
981
982 //put the chosen slave into slot, and adjust flags and state
983 if( assignedSlaveVP != NULL )
984 { currSlot->slaveAssignedToSlot = assignedSlaveVP;
985 assignedSlaveVP->animSlotAssignedTo = currSlot;
986 currSlot->needsSlaveAssigned = FALSE;
987 numSlotsFilled += 1;
988
989 HOLISTIC__Record_Assigner_end;
990 }
991 }//if slot needs slave assigned
992 }//for( slotIdx..
993
994 MEAS__Capture_Post_Master_Point;
995
996 masterSwitchToCoreCtlr( masterVP );
997 flushRegisters();
998 DEBUG__printf(FALSE,"came back after switch to core -- so lock released!");
999 }//while(1)
1000 } 1120 }
1001 1121
1122 /*When a task ends, are two scenarios: 1) task ran to completion, or 2) task
1123 * suspended at some point in its code.
1124 *For 1, just decr count of live tasks (and check for end condition) -- the
1125 * master loop will decide what goes into the slot freed up by this task end,
1126 * so, here, don't worry about assigning a new task to the slot slave.
1127 *For 2, the task's slot slave has been converted to a free task slave, which
1128 * now has nothing more to do, so send it to the recycle Q (which includes
1129 * freeing all the semData and meta task structs alloc'd for it). Then
1130 * decrement the live task count and check end condition.
1131 *
1132 *PR has to update count of live tasks, and check end of process condition.
1133 * There are constructs that wait for a process to end, so when end detected,
1134 * have to resume what's waiting..
1135 *Thing is, the wait is used in "main", so it's an OS thread. That means
1136 * PR internals have to do OS thread signaling. Want to do that in the
1137 * core controller, which has the original stack of an OS thread.
1138 *
1139 *So here, when detect process end, signal to the core controller, which will
1140 * then do the condition variable notify to the OS thread that's waiting.
1141 */
1142 inline void
1143 PRHandle_EndTask( SlaveVP *requestingSlv )
1144 { void *semEnv;
1145 PRReqst *req;
1146 PRMetaTask *metaTask;
1147 PRProcess *process;
1148
1149 req = requestingSlv->request;
1150 semEnv = PR_int__give_semEnv_of_req( req, requestingSlv ); //magic num in req
1151 metaTask = req->metaTask;
1152 //Want to keep PRMetaTask hidden from plugin, so extract semReq..
1153 (*req->handler)( metaTask, req->semReq, semEnv );
1154
1155 recycleFreeTaskSlave( requestingSlv );
1156
1157 process->numLiveTasks -= 1;
1158
1159 //check End Of Process Condition
1160 if( process->numLiveTasks == 0 &&
1161 process->numLiveGenericSlaves == 0 )
1162 signalEndOfProcessToCoreCtlr;
1163 }
1164
1165