seanhalle@64: /* seanhalle@64: * Copyright 2010 OpenSourceCodeStewardshipFoundation seanhalle@64: * seanhalle@64: * Licensed under BSD seanhalle@64: */ seanhalle@64: seanhalle@64: #include seanhalle@64: #include seanhalle@64: #include seanhalle@64: seanhalle@64: #include "Queue_impl/PrivateQueue.h" seanhalle@64: #include "Hash_impl/PrivateHash.h" seanhalle@64: seanhalle@64: #include "SSR.h" seanhalle@64: #include "SSR_Counter_Recording.h" seanhalle@64: seanhalle@64: //========================================================================== seanhalle@64: seanhalle@64: void seanhalle@64: SSR__init(); seanhalle@64: seanhalle@64: void seanhalle@64: SSR__init_Helper(); seanhalle@64: //========================================================================== seanhalle@64: seanhalle@64: seanhalle@64: /*TODO: Q: dealing with library f()s and DKU vs WT vs FoR seanhalle@64: * (still want to do FoR, with time-lines as syntax, could be super cool) seanhalle@64: * A: thinking pin the coreCtlrs for all of BLIS -- let Master arbitrate seanhalle@64: * among library, DKU, WT, FoR -- all the patterns in terms of virtual seanhalle@64: * processors (or equivalently work-units), so Master picks which virt procr seanhalle@67: * from which portions of app (DKU, WT, FoR) onto which anim slots seanhalle@67: *Might even do hierarchy of masters -- group of anim slots for each core seanhalle@64: * has its own master, that keeps generated work local seanhalle@64: * single-reader-single-writer sync everywhere -- no atomic primitives seanhalle@64: * Might have the different assigners talk to each other, to negotiate seanhalle@64: * larger-grain sharing of resources, according to predicted critical seanhalle@64: * path, and expansion of work seanhalle@64: */ seanhalle@64: seanhalle@64: seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: seanhalle@64: seanhalle@64: /*These are the library functions *called in the application* seanhalle@64: * seanhalle@64: *There's a pattern for the outside sequential code to interact with the seanhalle@64: * VMS_HW code. seanhalle@64: *The VMS_HW system is inside a boundary.. every SSR system is in its seanhalle@64: * own directory that contains the functions for each of the processor types. seanhalle@64: * One of the processor types is the "seed" processor that starts the seanhalle@64: * cascade of creating all the processors that do the work. seanhalle@64: *So, in the directory is a file called "EntryPoint.c" that contains the seanhalle@64: * function, named appropriately to the work performed, that the outside seanhalle@64: * sequential code calls. This function follows a pattern: seanhalle@64: *1) it calls SSR__init() seanhalle@64: *2) it creates the initial data for the seed processor, which is passed seanhalle@64: * in to the function seanhalle@64: *3) it creates the seed SSR processor, with the data to start it with. seanhalle@64: *4) it calls startSSRThenWaitUntilWorkDone seanhalle@64: *5) it gets the returnValue from the transfer struc and returns that seanhalle@64: * from the function seanhalle@64: * seanhalle@64: *For now, a new SSR system has to be created via SSR__init every seanhalle@64: * time an entry point function is called -- later, might add letting the seanhalle@64: * SSR system be created once, and let all the entry points just reuse seanhalle@64: * it -- want to be as simple as possible now, and see by using what makes seanhalle@64: * sense for later.. seanhalle@64: */ seanhalle@64: seanhalle@64: seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: seanhalle@64: /*This is the "border crossing" function -- the thing that crosses from the seanhalle@64: * outside world, into the VMS_HW world. It initializes and starts up the seanhalle@64: * VMS system, then creates one processor from the specified function and seanhalle@64: * puts it into the readyQ. From that point, that one function is resp. seanhalle@64: * for creating all the other processors, that then create others, and so seanhalle@64: * forth. seanhalle@64: *When all the processors, including the seed, have dissipated, then this seanhalle@64: * function returns. The results will have been written by side-effect via seanhalle@64: * pointers read from, or written into initData. seanhalle@64: * seanhalle@64: *NOTE: no Threads should exist in the outside program that might touch seanhalle@64: * any of the data reachable from initData passed in to here seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__create_seed_procr_and_do_work( TopLevelFnPtr fnPtr, void *initData ) seanhalle@64: { SSRSemEnv *semEnv; seanhalle@64: SlaveVP *seedPr; seanhalle@64: seanhalle@64: SSR__init(); //normal multi-thd seanhalle@64: seanhalle@64: semEnv = _VMSMasterEnv->semanticEnv; seanhalle@64: seanhalle@64: //SSR starts with one processor, which is put into initial environ, seanhalle@64: // and which then calls create() to create more, thereby expanding work seanhalle@64: seedPr = SSR__create_procr_helper( fnPtr, initData, seanhalle@64: semEnv, semEnv->nextCoreToGetNewPr++ ); seanhalle@64: seanhalle@64: resume_slaveVP( seedPr, semEnv ); seanhalle@64: seanhalle@64: VMS_SS__start_the_work_then_wait_until_done(); //normal multi-thd seanhalle@64: seanhalle@64: SSR__cleanup_after_shutdown(); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: int32 seanhalle@64: SSR__giveMinWorkUnitCycles( float32 percentOverhead ) seanhalle@64: { seanhalle@64: return MIN_WORK_UNIT_CYCLES; seanhalle@64: } seanhalle@64: seanhalle@64: int32 seanhalle@64: SSR__giveIdealNumWorkUnits() seanhalle@64: { seanhalle@69: return NUM_ANIM_SLOTS * NUM_CORES; seanhalle@64: } seanhalle@64: seanhalle@64: int32 seanhalle@64: SSR__give_number_of_cores_to_schedule_onto() seanhalle@64: { seanhalle@64: return NUM_CORES; seanhalle@64: } seanhalle@64: seanhalle@64: /*For now, use TSC -- later, make these two macros with assembly that first seanhalle@64: * saves jump point, and second jumps back several times to get reliable time seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__start_primitive() seanhalle@64: { saveLowTimeStampCountInto( ((SSRSemEnv *)(_VMSMasterEnv->semanticEnv))-> seanhalle@64: primitiveStartTime ); seanhalle@64: } seanhalle@64: seanhalle@64: /*Just quick and dirty for now -- make reliable later seanhalle@64: * will want this to jump back several times -- to be sure cache is warm seanhalle@64: * because don't want comm time included in calc-time measurement -- and seanhalle@64: * also to throw out any "weird" values due to OS interrupt or TSC rollover seanhalle@64: */ seanhalle@64: int32 seanhalle@64: SSR__end_primitive_and_give_cycles() seanhalle@64: { int32 endTime, startTime; seanhalle@64: //TODO: fix by repeating time-measurement seanhalle@64: saveLowTimeStampCountInto( endTime ); seanhalle@64: startTime =((SSRSemEnv*)(_VMSMasterEnv->semanticEnv))->primitiveStartTime; seanhalle@64: return (endTime - startTime); seanhalle@64: } seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: seanhalle@64: /*Initializes all the data-structures for a SSR system -- but doesn't seanhalle@64: * start it running yet! seanhalle@64: * seanhalle@64: *This runs in the main thread -- before VMS starts up seanhalle@64: * seanhalle@64: *This sets up the semantic layer over the VMS system seanhalle@64: * seanhalle@64: *First, calls VMS_Setup, then creates own environment, making it ready seanhalle@64: * for creating the seed processor and then starting the work. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__init() seanhalle@64: { seanhalle@64: VMS_SS__init(); seanhalle@64: //masterEnv, a global var, now is partially set up by init_VMS seanhalle@64: // after this, have VMS_int__malloc and VMS_int__free available seanhalle@64: seanhalle@64: SSR__init_Helper(); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: void idle_fn(void* data, SlaveVP *animatingSlv){ seanhalle@64: while(1){ seanhalle@64: VMS_int__suspend_slaveVP_and_send_req(animatingSlv); seanhalle@64: } seanhalle@64: } seanhalle@64: seanhalle@64: void seanhalle@64: SSR__init_Helper() seanhalle@64: { SSRSemEnv *semanticEnv; seanhalle@64: PrivQueueStruc **readyVPQs; seanhalle@64: int coreIdx, i, j; seanhalle@64: seanhalle@64: //Hook up the semantic layer's plug-ins to the Master virt procr seanhalle@64: _VMSMasterEnv->requestHandler = &SSR__Request_Handler; seanhalle@67: _VMSMasterEnv->slaveAssigner = &SSR__assign_slaveVP_to_slot; seanhalle@64: #ifdef HOLISTIC__TURN_ON_PERF_COUNTERS seanhalle@64: _VMSMasterEnv->counterHandler = &SSR__counter_handler; seanhalle@64: #endif seanhalle@64: seanhalle@64: //create the semantic layer's environment (all its data) and add to seanhalle@64: // the master environment seanhalle@64: semanticEnv = VMS_int__malloc( sizeof( SSRSemEnv ) ); seanhalle@64: _VMSMasterEnv->semanticEnv = semanticEnv; seanhalle@64: seanhalle@64: #ifdef HOLISTIC__TURN_ON_PERF_COUNTERS seanhalle@64: SSR__init_counter_data_structs(); seanhalle@64: #endif nengel@72: #ifdef IDLE_SLAVES nengel@66: semanticEnv->shutdownInitiated = FALSE; seanhalle@64: for(i=0;iidlePr[i][j] = VMS_int__create_slaveVP(&idle_fn,NULL); seanhalle@64: semanticEnv->idlePr[i][j]->coreAnimatedBy = i; nengel@72: semanticEnv->idlePr[i][j]->typeOfVP = Idle; seanhalle@64: } seanhalle@64: } nengel@72: #endif seanhalle@64: #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC seanhalle@64: semanticEnv->unitList = makeListOfArrays(sizeof(Unit),128); seanhalle@64: semanticEnv->ctlDependenciesList = makeListOfArrays(sizeof(Dependency),128); seanhalle@64: semanticEnv->commDependenciesList = makeListOfArrays(sizeof(Dependency),128); seanhalle@64: semanticEnv->dynDependenciesList = makeListOfArrays(sizeof(Dependency),128); nengel@74: semanticEnv->singletonDependenciesList = makeListOfArrays(sizeof(Dependency),128); seanhalle@64: semanticEnv->ntonGroupsInfo = makePrivDynArrayOfSize((void***)&(semanticEnv->ntonGroups),8); seanhalle@64: seanhalle@64: semanticEnv->hwArcs = makeListOfArrays(sizeof(Dependency),128); seanhalle@69: memset(semanticEnv->last_in_slot,0,sizeof(NUM_CORES * NUM_ANIM_SLOTS * sizeof(Unit))); seanhalle@64: #endif seanhalle@64: seanhalle@64: //create the ready queue, hash tables used for pairing send to receive seanhalle@64: // and so forth seanhalle@64: //TODO: add hash tables for pairing sends with receives, and seanhalle@64: // initialize the data ownership system seanhalle@64: readyVPQs = VMS_int__malloc( NUM_CORES * sizeof(PrivQueueStruc *) ); seanhalle@64: seanhalle@64: for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) seanhalle@64: { seanhalle@64: readyVPQs[ coreIdx ] = makeVMSQ(); seanhalle@64: } seanhalle@64: seanhalle@64: semanticEnv->readyVPQs = readyVPQs; seanhalle@64: seanhalle@64: semanticEnv->nextCoreToGetNewPr = 0; seanhalle@64: semanticEnv->numSlaveVP = 0; seanhalle@64: seanhalle@64: semanticEnv->commHashTbl = makeHashTable( 1<<16, &VMS_int__free );//start big seanhalle@64: seanhalle@64: //TODO: bug -- turn these arrays into dyn arrays to eliminate limit seanhalle@64: //semanticEnv->singletonHasBeenExecutedFlags = makeDynArrayInfo( ); seanhalle@64: //semanticEnv->transactionStrucs = makeDynArrayInfo( ); seanhalle@64: for( i = 0; i < NUM_STRUCS_IN_SEM_ENV; i++ ) seanhalle@64: { seanhalle@64: semanticEnv->fnSingletons[i].endInstrAddr = NULL; seanhalle@64: semanticEnv->fnSingletons[i].hasBeenStarted = FALSE; seanhalle@64: semanticEnv->fnSingletons[i].hasFinished = FALSE; seanhalle@64: semanticEnv->fnSingletons[i].waitQ = makeVMSQ(); seanhalle@64: semanticEnv->transactionStrucs[i].waitingVPQ = makeVMSQ(); seanhalle@64: } seanhalle@82: seanhalle@82: VMSCommNode * seanhalle@82: systemNode = VMS_SS__give_comm_hierarchy(); //this is read only!! seanhalle@82: seanhalle@82: //Do something with the comm system here.. make own, faster, data seanhalle@82: // structure that is used by assigner -- it can include info about seanhalle@82: // measured miss rates, and structures that track the data.. seanhalle@82: //Next step would be to take a shot at a function call to put into the seanhalle@82: // application that gives a "name" to collection of data consumed by seanhalle@82: // a work-unit, and a name to the data produced.. along with the size seanhalle@82: // each of those named collections. seanhalle@82: //Then, can come up with a way to represent how much of each seanhalle@82: // named data collection that resides in each of the caches. Keep that seanhalle@82: // representation in the data structure that build from parsing the seanhalle@82: // comm system returned from VMS. seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: /*Frees any memory allocated by SSR__init() then calls VMS_int__shutdown seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__cleanup_after_shutdown() seanhalle@64: { SSRSemEnv *semanticEnv; seanhalle@64: seanhalle@64: semanticEnv = _VMSMasterEnv->semanticEnv; seanhalle@64: seanhalle@64: #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC seanhalle@64: //UCC seanhalle@64: FILE* output; seanhalle@64: int n; seanhalle@64: char filename[255]; seanhalle@64: for(n=0;n<255;n++) seanhalle@64: { seanhalle@64: sprintf(filename, "./counters/UCC.%d",n); seanhalle@64: output = fopen(filename,"r"); seanhalle@64: if(output) seanhalle@64: { seanhalle@64: fclose(output); seanhalle@64: }else{ seanhalle@64: break; seanhalle@64: } seanhalle@64: } seanhalle@64: if(n<255){ seanhalle@64: printf("Saving UCC to File: %s ...\n", filename); seanhalle@64: output = fopen(filename,"w+"); seanhalle@64: if(output!=NULL){ seanhalle@64: set_dependency_file(output); seanhalle@64: //fprintf(output,"digraph Dependencies {\n"); seanhalle@64: //set_dot_file(output); seanhalle@64: //FIXME: first line still depends on counters being enabled, replace w/ unit struct! seanhalle@64: //forAllInDynArrayDo(_VMSMasterEnv->counter_history_array_info, &print_dot_node_info ); seanhalle@64: forAllInListOfArraysDo(semanticEnv->unitList, &print_unit_to_file); seanhalle@64: forAllInListOfArraysDo( semanticEnv->commDependenciesList, &print_comm_dependency_to_file ); seanhalle@64: forAllInListOfArraysDo( semanticEnv->ctlDependenciesList, &print_ctl_dependency_to_file ); seanhalle@64: forAllInDynArrayDo(semanticEnv->ntonGroupsInfo,&print_nton_to_file); seanhalle@64: //fprintf(output,"}\n"); seanhalle@64: fflush(output); seanhalle@64: seanhalle@64: } else seanhalle@64: printf("Opening UCC file failed. Please check that folder \"counters\" exists in run directory and has write permission.\n"); seanhalle@64: } else { seanhalle@64: printf("Could not open UCC file, please clean \"counters\" folder. (Must contain less than 255 files.)\n"); seanhalle@64: } seanhalle@64: //Loop Graph seanhalle@64: for(n=0;n<255;n++) seanhalle@64: { seanhalle@64: sprintf(filename, "./counters/LoopGraph.%d",n); seanhalle@64: output = fopen(filename,"r"); seanhalle@64: if(output) seanhalle@64: { seanhalle@64: fclose(output); seanhalle@64: }else{ seanhalle@64: break; seanhalle@64: } seanhalle@64: } seanhalle@64: if(n<255){ seanhalle@64: printf("Saving LoopGraph to File: %s ...\n", filename); seanhalle@64: output = fopen(filename,"w+"); seanhalle@64: if(output!=NULL){ seanhalle@64: set_dependency_file(output); seanhalle@64: //fprintf(output,"digraph Dependencies {\n"); seanhalle@64: //set_dot_file(output); seanhalle@64: //FIXME: first line still depends on counters being enabled, replace w/ unit struct! seanhalle@64: //forAllInDynArrayDo(_VMSMasterEnv->counter_history_array_info, &print_dot_node_info ); seanhalle@64: forAllInListOfArraysDo( semanticEnv->unitList, &print_unit_to_file ); seanhalle@64: forAllInListOfArraysDo( semanticEnv->commDependenciesList, &print_comm_dependency_to_file ); seanhalle@64: forAllInListOfArraysDo( semanticEnv->ctlDependenciesList, &print_ctl_dependency_to_file ); seanhalle@64: forAllInListOfArraysDo( semanticEnv->dynDependenciesList, &print_dyn_dependency_to_file ); nengel@74: forAllInListOfArraysDo( semanticEnv->singletonDependenciesList, &print_singleton_dependency_to_file ); seanhalle@64: forAllInListOfArraysDo( semanticEnv->hwArcs, &print_hw_dependency_to_file ); seanhalle@64: //fprintf(output,"}\n"); seanhalle@64: fflush(output); seanhalle@64: seanhalle@64: } else seanhalle@64: printf("Opening LoopGraph file failed. Please check that folder \"counters\" exists in run directory and has write permission.\n"); seanhalle@64: } else { seanhalle@64: printf("Could not open LoopGraph file, please clean \"counters\" folder. (Must contain less than 255 files.)\n"); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: freeListOfArrays(semanticEnv->unitList); seanhalle@64: freeListOfArrays(semanticEnv->commDependenciesList); seanhalle@64: freeListOfArrays(semanticEnv->ctlDependenciesList); seanhalle@64: freeListOfArrays(semanticEnv->dynDependenciesList); nengel@74: freeListOfArrays(semanticEnv->singletonDependenciesList); seanhalle@64: #endif seanhalle@64: #ifdef HOLISTIC__TURN_ON_PERF_COUNTERS seanhalle@64: for(n=0;n<255;n++) seanhalle@64: { seanhalle@64: sprintf(filename, "./counters/Counters.%d.csv",n); seanhalle@64: output = fopen(filename,"r"); seanhalle@64: if(output) seanhalle@64: { seanhalle@64: fclose(output); seanhalle@64: }else{ seanhalle@64: break; seanhalle@64: } seanhalle@64: } seanhalle@64: if(n<255){ seanhalle@64: printf("Saving Counter measurements to File: %s ...\n", filename); seanhalle@64: output = fopen(filename,"w+"); seanhalle@64: if(output!=NULL){ seanhalle@64: set_counter_file(output); seanhalle@64: int i; seanhalle@64: for(i=0;icounterList[i], &print_counter_events_to_file ); seanhalle@64: fflush(output); seanhalle@64: } seanhalle@64: seanhalle@64: } else seanhalle@64: printf("Opening UCC file failed. Please check that folder \"counters\" exists in run directory and has write permission.\n"); seanhalle@64: } else { seanhalle@64: printf("Could not open UCC file, please clean \"counters\" folder. (Must contain less than 255 files.)\n"); seanhalle@64: } seanhalle@64: seanhalle@64: #endif seanhalle@64: /* It's all allocated inside VMS's big chunk -- that's about to be freed, so seanhalle@64: * nothing to do here seanhalle@64: seanhalle@64: seanhalle@64: for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) seanhalle@64: { seanhalle@64: VMS_int__free( semanticEnv->readyVPQs[coreIdx]->startOfData ); seanhalle@64: VMS_int__free( semanticEnv->readyVPQs[coreIdx] ); seanhalle@64: } seanhalle@64: VMS_int__free( semanticEnv->readyVPQs ); seanhalle@64: seanhalle@64: freeHashTable( semanticEnv->commHashTbl ); seanhalle@64: VMS_int__free( _VMSMasterEnv->semanticEnv ); seanhalle@64: */ seanhalle@64: VMS_SS__cleanup_at_end_of_shutdown(); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: seanhalle@64: /* seanhalle@64: */ seanhalle@64: SlaveVP * seanhalle@64: SSR__create_procr_with( TopLevelFnPtr fnPtr, void *initData, seanhalle@64: SlaveVP *creatingPr ) seanhalle@64: { SSRSemReq reqData; seanhalle@64: seanhalle@64: //the semantic request data is on the stack and disappears when this seanhalle@64: // call returns -- it's guaranteed to remain in the VP's stack for as seanhalle@64: // long as the VP is suspended. seanhalle@64: reqData.reqType = 0; //know type because in a VMS create req seanhalle@64: reqData.coreToAssignOnto = -1; //means round-robin assign seanhalle@64: reqData.fnPtr = fnPtr; seanhalle@64: reqData.initData = initData; seanhalle@64: reqData.sendPr = creatingPr; seanhalle@64: seanhalle@64: VMS_WL__send_create_slaveVP_req( &reqData, creatingPr ); seanhalle@64: seanhalle@64: return creatingPr->dataRetFromReq; seanhalle@64: } seanhalle@64: seanhalle@64: SlaveVP * seanhalle@64: SSR__create_procr_with_affinity( TopLevelFnPtr fnPtr, void *initData, seanhalle@64: SlaveVP *creatingPr, int32 coreToAssignOnto ) seanhalle@64: { SSRSemReq reqData; seanhalle@64: seanhalle@64: //the semantic request data is on the stack and disappears when this seanhalle@64: // call returns -- it's guaranteed to remain in the VP's stack for as seanhalle@64: // long as the VP is suspended. seanhalle@64: reqData.reqType = 0; //know type because in a VMS create req seanhalle@64: reqData.coreToAssignOnto = coreToAssignOnto; seanhalle@64: reqData.fnPtr = fnPtr; seanhalle@64: reqData.initData = initData; seanhalle@64: reqData.sendPr = creatingPr; seanhalle@64: seanhalle@64: VMS_WL__send_create_slaveVP_req( &reqData, creatingPr ); seanhalle@64: seanhalle@64: return creatingPr->dataRetFromReq; seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: void seanhalle@64: SSR__dissipate_procr( SlaveVP *procrToDissipate ) seanhalle@64: { seanhalle@64: VMS_WL__send_dissipate_req( procrToDissipate ); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: seanhalle@64: void * seanhalle@64: SSR__malloc_to( int32 sizeToMalloc, SlaveVP *owningPr ) seanhalle@64: { SSRSemReq reqData; seanhalle@64: seanhalle@64: reqData.reqType = malloc_req; seanhalle@64: reqData.sendPr = owningPr; seanhalle@64: reqData.sizeToMalloc = sizeToMalloc; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, owningPr ); seanhalle@64: seanhalle@64: return owningPr->dataRetFromReq; seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: /*Sends request to Master, which does the work of freeing seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__free( void *ptrToFree, SlaveVP *owningPr ) seanhalle@64: { SSRSemReq reqData; seanhalle@64: seanhalle@64: reqData.reqType = free_req; seanhalle@64: reqData.sendPr = owningPr; seanhalle@64: reqData.ptrToFree = ptrToFree; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, owningPr ); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: void seanhalle@64: SSR__transfer_ownership_of_from_to( void *data, SlaveVP *oldOwnerSlv, seanhalle@64: SlaveVP *newOwnerPr ) seanhalle@64: { seanhalle@64: //TODO: put in the ownership system that automatically frees when no seanhalle@64: // owners of data left -- will need keeper for keeping data around when seanhalle@64: // future created processors might need it but don't exist yet seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: void seanhalle@64: SSR__add_ownership_by_to( SlaveVP *newOwnerSlv, void *data ) seanhalle@64: { seanhalle@64: seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: void seanhalle@64: SSR__remove_ownership_by_from( SlaveVP *loserSlv, void *dataLosing ) seanhalle@64: { seanhalle@64: seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: /*Causes the SSR system to remove internal ownership, so data won't be seanhalle@64: * freed when SSR shuts down, and will persist in the external program. seanhalle@64: * seanhalle@64: *Must be called from the processor that currently owns the data. seanhalle@64: * seanhalle@64: *IMPL: Transferring ownership touches two different virtual processor's seanhalle@64: * state -- which means it has to be done carefully -- the VMS rules for seanhalle@64: * semantic layers say that a work-unit is only allowed to touch the seanhalle@64: * virtual processor it is part of, and that only a single work-unit per seanhalle@64: * virtual processor be assigned to a slave at a time. So, this has to seanhalle@64: * modify the virtual processor that owns the work-unit that called this seanhalle@64: * function, then create a request to have the other processor modified. seanhalle@64: *However, in this case, the TO processor is the outside, and transfers seanhalle@64: * are only allowed to be called by the giver-upper, so can mark caller of seanhalle@64: * this function as no longer owner, and return -- done. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__transfer_ownership_to_outside( void *data ) seanhalle@64: { seanhalle@64: //TODO: removeAllOwnersFrom( data ); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: seanhalle@64: void seanhalle@64: SSR__send_of_type_to( SlaveVP *sendPr, void *msg, const int type, seanhalle@64: SlaveVP *receivePr) seanhalle@64: { SSRSemReq reqData; seanhalle@64: seanhalle@64: reqData.receivePr = receivePr; seanhalle@64: reqData.sendPr = sendPr; seanhalle@64: reqData.reqType = send_type; seanhalle@64: reqData.msgType = type; seanhalle@64: reqData.msg = msg; seanhalle@64: reqData.nextReqInHashEntry = NULL; seanhalle@64: seanhalle@64: //On ownership -- remove inside the send and let ownership sit in limbo seanhalle@64: // as a potential in an entry in the hash table, when this receive msg seanhalle@64: // gets paired to a send, the ownership gets added to the receivePr -- seanhalle@64: // the next work-unit in the receivePr's trace will have ownership. seanhalle@64: VMS_WL__send_sem_request( &reqData, sendPr ); seanhalle@64: seanhalle@64: //When come back from suspend, no longer own data reachable from msg seanhalle@64: //TODO: release ownership here seanhalle@64: } seanhalle@64: seanhalle@64: void seanhalle@64: SSR__send_from_to( void *msg, SlaveVP *sendPr, SlaveVP *receivePr ) seanhalle@64: { SSRSemReq reqData; seanhalle@64: seanhalle@64: //hash on the receiver, 'cause always know it, but sometimes want to seanhalle@64: // receive from anonymous sender seanhalle@64: seanhalle@64: reqData.receivePr = receivePr; seanhalle@64: reqData.sendPr = sendPr; seanhalle@64: reqData.reqType = send_from_to; seanhalle@64: reqData.msg = msg; seanhalle@64: reqData.nextReqInHashEntry = NULL; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, sendPr ); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: seanhalle@64: void * seanhalle@64: SSR__receive_any_to( SlaveVP *receivePr ) seanhalle@64: { seanhalle@64: seanhalle@64: } seanhalle@64: seanhalle@64: void * seanhalle@64: SSR__receive_type_to( const int type, SlaveVP *receivePr ) seanhalle@67: { DEBUG__printf1(dbgRqstHdlr,"WL: receive type to: %d", receivePr->slaveID); seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: reqData.receivePr = receivePr; seanhalle@64: reqData.reqType = receive_type; seanhalle@64: reqData.msgType = type; seanhalle@64: reqData.nextReqInHashEntry = NULL; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, receivePr ); seanhalle@64: seanhalle@64: return receivePr->dataRetFromReq; seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: seanhalle@64: /*Call this at point receiving virt pr wants in-coming data. seanhalle@64: * seanhalle@64: *The reason receivePr must call this is that it modifies the receivPr seanhalle@64: * loc structure directly -- and the VMS rules state a virtual processor seanhalle@64: * loc structure can only be modified by itself. seanhalle@64: */ seanhalle@64: void * seanhalle@64: SSR__receive_from_to( SlaveVP *sendPr, SlaveVP *receivePr ) seanhalle@67: { DEBUG__printf2(dbgRqstHdlr,"WL: receive from %d to: %d", sendPr->slaveID, receivePr->slaveID); seanhalle@67: SSRSemReq reqData; seanhalle@64: seanhalle@64: //hash on the receiver, 'cause always know it, but sometimes want to seanhalle@64: // receive from anonymous sender seanhalle@64: seanhalle@64: reqData.receivePr = receivePr; seanhalle@64: reqData.sendPr = sendPr; seanhalle@64: reqData.reqType = receive_from_to; seanhalle@64: reqData.nextReqInHashEntry = NULL; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, receivePr ); seanhalle@64: seanhalle@64: return receivePr->dataRetFromReq; seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: //=========================================================================== seanhalle@64: // seanhalle@64: /*A function singleton is a function whose body executes exactly once, on a seanhalle@64: * single core, no matter how many times the fuction is called and no seanhalle@64: * matter how many cores or the timing of cores calling it. seanhalle@64: * seanhalle@64: *A data singleton is a ticket attached to data. That ticket can be used seanhalle@64: * to get the data through the function exactly once, no matter how many seanhalle@64: * times the data is given to the function, and no matter the timing of seanhalle@64: * trying to get the data through from different cores. seanhalle@64: */ seanhalle@64: seanhalle@64: /*asm function declarations*/ seanhalle@64: void asm_save_ret_to_singleton(SSRSingleton *singletonPtrAddr); seanhalle@64: void asm_write_ret_from_singleton(SSRSingleton *singletonPtrAddr); seanhalle@64: seanhalle@64: /*Fn singleton uses ID as index into array of singleton structs held in the seanhalle@64: * semantic environment. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__start_fn_singleton( int32 singletonID, SlaveVP *animPr ) seanhalle@64: { seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: // seanhalle@64: reqData.reqType = singleton_fn_start; seanhalle@64: reqData.singletonID = singletonID; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, animPr ); seanhalle@64: if( animPr->dataRetFromReq ) //will be 0 or addr of label in end singleton seanhalle@64: { seanhalle@64: SSRSemEnv *semEnv = VMS_int__give_sem_env_for( animPr ); seanhalle@64: asm_write_ret_from_singleton(&(semEnv->fnSingletons[ singletonID])); seanhalle@64: } seanhalle@64: } seanhalle@64: seanhalle@64: /*Data singleton hands addr of loc holding a pointer to a singleton struct. seanhalle@64: * The start_data_singleton makes the structure and puts its addr into the seanhalle@64: * location. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__start_data_singleton( SSRSingleton **singletonAddr, SlaveVP *animPr ) seanhalle@64: { seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: if( *singletonAddr && (*singletonAddr)->hasFinished ) seanhalle@64: goto JmpToEndSingleton; seanhalle@64: seanhalle@64: reqData.reqType = singleton_data_start; seanhalle@64: reqData.singletonPtrAddr = singletonAddr; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, animPr ); seanhalle@64: if( animPr->dataRetFromReq ) //either 0 or end singleton's return addr seanhalle@64: { //Assembly code changes the return addr on the stack to the one seanhalle@64: // saved into the singleton by the end-singleton-fn seanhalle@64: //The return addr is at 0x4(%%ebp) seanhalle@64: JmpToEndSingleton: seanhalle@64: asm_write_ret_from_singleton(*singletonAddr); seanhalle@64: } seanhalle@64: //now, simply return seanhalle@64: //will exit either from the start singleton call or the end-singleton call seanhalle@64: } seanhalle@64: seanhalle@64: /*Uses ID as index into array of flags. If flag already set, resumes from seanhalle@64: * end-label. Else, sets flag and resumes normally. seanhalle@64: * seanhalle@64: *Note, this call cannot be inlined because the instr addr at the label seanhalle@64: * inside is shared by all invocations of a given singleton ID. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__end_fn_singleton( int32 singletonID, SlaveVP *animPr ) seanhalle@64: { seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: //don't need this addr until after at least one singleton has reached seanhalle@64: // this function seanhalle@64: SSRSemEnv *semEnv = VMS_int__give_sem_env_for( animPr ); seanhalle@64: asm_write_ret_from_singleton(&(semEnv->fnSingletons[ singletonID])); seanhalle@64: seanhalle@64: reqData.reqType = singleton_fn_end; seanhalle@64: reqData.singletonID = singletonID; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, animPr ); seanhalle@64: seanhalle@64: EndSingletonInstrAddr: seanhalle@64: return; seanhalle@64: } seanhalle@64: seanhalle@64: void seanhalle@64: SSR__end_data_singleton( SSRSingleton **singletonPtrAddr, SlaveVP *animPr ) seanhalle@64: { seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: //don't need this addr until after singleton struct has reached seanhalle@64: // this function for first time seanhalle@64: //do assembly that saves the return addr of this fn call into the seanhalle@64: // data singleton -- that data-singleton can only be given to exactly seanhalle@64: // one instance in the code of this function. However, can use this seanhalle@64: // function in different places for different data-singletons. seanhalle@64: // (*(singletonAddr))->endInstrAddr = &&EndDataSingletonInstrAddr; seanhalle@64: seanhalle@64: seanhalle@64: asm_save_ret_to_singleton(*singletonPtrAddr); seanhalle@64: seanhalle@64: reqData.reqType = singleton_data_end; seanhalle@64: reqData.singletonPtrAddr = singletonPtrAddr; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, animPr ); seanhalle@64: } seanhalle@64: seanhalle@64: /*This executes the function in the masterVP, so it executes in isolation seanhalle@64: * from any other copies -- only one copy of the function can ever execute seanhalle@64: * at a time. seanhalle@64: * seanhalle@64: *It suspends to the master, and the request handler takes the function seanhalle@64: * pointer out of the request and calls it, then resumes the VP. seanhalle@64: *Only very short functions should be called this way -- for longer-running seanhalle@64: * isolation, use transaction-start and transaction-end, which run the code seanhalle@64: * between as work-code. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__animate_short_fn_in_isolation( PtrToAtomicFn ptrToFnToExecInMaster, seanhalle@64: void *data, SlaveVP *animPr ) seanhalle@64: { seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: // seanhalle@64: reqData.reqType = atomic; seanhalle@64: reqData.fnToExecInMaster = ptrToFnToExecInMaster; seanhalle@64: reqData.dataForFn = data; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, animPr ); seanhalle@64: } seanhalle@64: seanhalle@64: seanhalle@64: /*This suspends to the master. seanhalle@64: *First, it looks at the VP's data, to see the highest transactionID that VP seanhalle@64: * already has entered. If the current ID is not larger, it throws an seanhalle@64: * exception stating a bug in the code. Otherwise it puts the current ID seanhalle@64: * there, and adds the ID to a linked list of IDs entered -- the list is seanhalle@64: * used to check that exits are properly ordered. seanhalle@64: *Next it is uses transactionID as index into an array of transaction seanhalle@64: * structures. seanhalle@64: *If the "VP_currently_executing" field is non-null, then put requesting VP seanhalle@64: * into queue in the struct. (At some point a holder will request seanhalle@64: * end-transaction, which will take this VP from the queue and resume it.) seanhalle@64: *If NULL, then write requesting into the field and resume. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__start_transaction( int32 transactionID, SlaveVP *animPr ) seanhalle@64: { seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: // seanhalle@64: reqData.sendPr = animPr; seanhalle@64: reqData.reqType = trans_start; seanhalle@64: reqData.transID = transactionID; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, animPr ); seanhalle@64: } seanhalle@64: seanhalle@64: /*This suspends to the master, then uses transactionID as index into an seanhalle@64: * array of transaction structures. seanhalle@64: *It looks at VP_currently_executing to be sure it's same as requesting VP. seanhalle@64: * If different, throws an exception, stating there's a bug in the code. seanhalle@64: *Next it looks at the queue in the structure. seanhalle@64: *If it's empty, it sets VP_currently_executing field to NULL and resumes. seanhalle@64: *If something in, gets it, sets VP_currently_executing to that VP, then seanhalle@64: * resumes both. seanhalle@64: */ seanhalle@64: void seanhalle@64: SSR__end_transaction( int32 transactionID, SlaveVP *animPr ) seanhalle@64: { seanhalle@64: SSRSemReq reqData; seanhalle@64: seanhalle@64: // seanhalle@64: reqData.sendPr = animPr; seanhalle@64: reqData.reqType = trans_end; seanhalle@64: reqData.transID = transactionID; seanhalle@64: seanhalle@64: VMS_WL__send_sem_request( &reqData, animPr ); seanhalle@64: }