Me@0: /* Me@38: * Copyright 2010 OpenSourceStewardshipFoundation Me@0: * Me@0: * Licensed under BSD Me@0: */ Me@0: Me@0: #include Me@0: #include Me@50: #include Me@0: #include msach@76: #include Me@50: #include Me@0: Me@0: #include "VMS.h" msach@77: #include "ProcrContext.h" Me@0: #include "Queue_impl/BlockingQueue.h" Me@38: #include "Histogram/Histogram.h" Me@0: Me@0: Me@26: #define thdAttrs NULL Me@26: Me@22: //=========================================================================== Me@22: void Me@22: shutdownFn( void *dummy, VirtProcr *dummy2 ); Me@22: Me@31: SchedSlot ** Me@31: create_sched_slots(); Me@22: Me@28: void Me@28: create_masterEnv(); Me@28: Me@28: void Me@28: create_the_coreLoop_OS_threads(); Me@28: Me@50: MallocProlog * Me@50: create_free_list(); Me@50: Me@53: void Me@53: endOSThreadFn( void *initData, VirtProcr *animatingPr ); Me@50: Me@26: pthread_mutex_t suspendLock = PTHREAD_MUTEX_INITIALIZER; Me@26: pthread_cond_t suspend_cond = PTHREAD_COND_INITIALIZER; Me@26: Me@22: //=========================================================================== Me@22: Me@0: /*Setup has two phases: Me@0: * 1) Semantic layer first calls init_VMS, which creates masterEnv, and puts Me@8: * the master virt procr into the work-queue, ready for first "call" Me@8: * 2) Semantic layer then does its own init, which creates the seed virt Me@8: * procr inside the semantic layer, ready to schedule it when Me@0: * asked by the first run of the masterLoop. Me@0: * Me@0: *This part is bit weird because VMS really wants to be "always there", and Me@0: * have applications attach and detach.. for now, this VMS is part of Me@0: * the app, so the VMS system starts up as part of running the app. Me@0: * Me@8: *The semantic layer is isolated from the VMS internals by making the Me@8: * semantic layer do setup to a state that it's ready with its Me@8: * initial virt procrs, ready to schedule them to slots when the masterLoop Me@0: * asks. Without this pattern, the semantic layer's setup would Me@8: * have to modify slots directly to assign the initial virt-procrs, and put Me@31: * them into the readyToAnimateQ itself, breaking the isolation completely. Me@0: * Me@0: * Me@8: *The semantic layer creates the initial virt procr(s), and adds its Me@8: * own environment to masterEnv, and fills in the pointers to Me@0: * the requestHandler and slaveScheduler plug-in functions Me@8: */ Me@8: Me@8: /*This allocates VMS data structures, populates the master VMSProc, Me@0: * and master environment, and returns the master environment to the semantic Me@0: * layer. Me@0: */ Me@8: void Me@8: VMS__init() Me@28: { Me@28: create_masterEnv(); Me@28: create_the_coreLoop_OS_threads(); Me@28: } Me@28: msach@71: #ifdef SEQUENTIAL msach@71: Me@28: /*To initialize the sequential version, just don't create the threads Me@28: */ Me@28: void Me@28: VMS__init_Seq() Me@28: { Me@28: create_masterEnv(); Me@28: } Me@28: msach@71: #endif msach@71: Me@28: void Me@28: create_masterEnv() Me@31: { MasterEnv *masterEnv; Me@55: VMSQueueStruc **readyToAnimateQs; Me@31: int coreIdx; Me@31: VirtProcr **masterVPs; Me@31: SchedSlot ***allSchedSlots; //ptr to array of ptrs Me@53: Me@53: Me@31: //Make the master env, which holds everything else Me@1: _VMSMasterEnv = malloc( sizeof(MasterEnv) ); Me@53: Me@53: //Very first thing put into the master env is the free-list, seeded Me@53: // with a massive initial chunk of memory. Me@53: //After this, all other mallocs are VMS__malloc. Me@53: _VMSMasterEnv->freeListHead = VMS_ext__create_free_list(); Me@53: Me@65: Me@65: //============================= MEASUREMENT STUFF ======================== Me@65: #ifdef MEAS__TIME_MALLOC msach@160: _VMSMasterEnv->mallocTimeHist = makeFixedBinHistExt( 100, 0, 30, msach@79: "malloc_time_hist"); msach@160: _VMSMasterEnv->freeTimeHist = makeFixedBinHistExt( 100, 0, 30, msach@79: "free_time_hist"); Me@65: #endif Me@68: #ifdef MEAS__TIME_PLUGIN msach@160: _VMSMasterEnv->reqHdlrLowTimeHist = makeFixedBinHistExt( 100, 0, 200, msach@79: "plugin_low_time_hist"); msach@160: _VMSMasterEnv->reqHdlrHighTimeHist = makeFixedBinHistExt( 100, 0, 200, msach@79: "plugin_high_time_hist"); Me@68: #endif Me@65: //======================================================================== Me@65: Me@53: //===================== Only VMS__malloc after this ==================== msach@69: masterEnv = (MasterEnv*)_VMSMasterEnv; Me@31: Me@31: //Make a readyToAnimateQ for each core loop Me@55: readyToAnimateQs = VMS__malloc( NUM_CORES * sizeof(VMSQueueStruc *) ); Me@53: masterVPs = VMS__malloc( NUM_CORES * sizeof(VirtProcr *) ); Me@0: Me@31: //One array for each core, 3 in array, core's masterVP scheds all Me@53: allSchedSlots = VMS__malloc( NUM_CORES * sizeof(SchedSlot *) ); Me@0: Me@53: _VMSMasterEnv->numProcrsCreated = 0; //used by create procr Me@31: for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) Me@53: { Me@55: readyToAnimateQs[ coreIdx ] = makeVMSQ(); Me@31: Me@50: //Q: should give masterVP core-specific info as its init data? msach@76: masterVPs[ coreIdx ] = VMS__create_procr( (VirtProcrFnPtr)&masterLoop, (void*)masterEnv ); Me@31: masterVPs[ coreIdx ]->coreAnimatedBy = coreIdx; Me@31: allSchedSlots[ coreIdx ] = create_sched_slots(); //makes for one core Me@53: _VMSMasterEnv->numMasterInARow[ coreIdx ] = 0; Me@55: _VMSMasterEnv->workStealingGates[ coreIdx ] = NULL; Me@31: } Me@31: _VMSMasterEnv->readyToAnimateQs = readyToAnimateQs; Me@31: _VMSMasterEnv->masterVPs = masterVPs; Me@50: _VMSMasterEnv->masterLock = UNLOCKED; Me@31: _VMSMasterEnv->allSchedSlots = allSchedSlots; Me@55: _VMSMasterEnv->workStealingLock = UNLOCKED; Me@28: Me@12: Me@31: //Aug 19, 2010: no longer need to place initial masterVP into queue Me@31: // because coreLoop now controls -- animates its masterVP when no work Me@31: Me@30: Me@50: //============================= MEASUREMENT STUFF ======================== Me@50: #ifdef STATS__TURN_ON_PROBES Me@50: _VMSMasterEnv->dynIntervalProbesInfo = msach@69: makePrivDynArrayOfSize( (void***)&(_VMSMasterEnv->intervalProbes), 200); Me@30: Me@53: _VMSMasterEnv->probeNameHashTbl = makeHashTable( 1000, &VMS__free ); Me@53: Me@53: //put creation time directly into master env, for fast retrieval Me@50: struct timeval timeStamp; Me@50: gettimeofday( &(timeStamp), NULL); Me@50: _VMSMasterEnv->createPtInSecs = Me@50: timeStamp.tv_sec +(timeStamp.tv_usec/1000000.0); Me@50: #endif Me@65: #ifdef MEAS__TIME_MASTER_LOCK Me@65: _VMSMasterEnv->masterLockLowTimeHist = makeFixedBinHist( 50, 0, 2, Me@65: "master lock low time hist"); Me@68: _VMSMasterEnv->masterLockHighTimeHist = makeFixedBinHist( 50, 0, 100, Me@65: "master lock high time hist"); Me@65: #endif Me@68: msach@76: MakeTheMeasHists(); Me@50: //======================================================================== Me@38: Me@0: } Me@0: Me@31: SchedSlot ** Me@31: create_sched_slots() Me@31: { SchedSlot **schedSlots; Me@0: int i; Me@0: Me@53: schedSlots = VMS__malloc( NUM_SCHED_SLOTS * sizeof(SchedSlot *) ); Me@8: Me@1: for( i = 0; i < NUM_SCHED_SLOTS; i++ ) Me@0: { Me@53: schedSlots[i] = VMS__malloc( sizeof(SchedSlot) ); Me@8: Me@1: //Set state to mean "handling requests done, slot needs filling" Me@8: schedSlots[i]->workIsDone = FALSE; Me@8: schedSlots[i]->needsProcrAssigned = TRUE; Me@0: } Me@31: return schedSlots; Me@31: } Me@31: Me@31: Me@31: void Me@31: freeSchedSlots( SchedSlot **schedSlots ) Me@31: { int i; Me@31: for( i = 0; i < NUM_SCHED_SLOTS; i++ ) Me@31: { Me@53: VMS__free( schedSlots[i] ); Me@31: } Me@53: VMS__free( schedSlots ); Me@0: } Me@0: Me@8: Me@28: void Me@28: create_the_coreLoop_OS_threads() Me@28: { Me@28: //======================================================================== Me@28: // Create the Threads Me@28: int coreIdx, retCode; Me@28: Me@28: //Need the threads to be created suspended, and wait for a signal Me@28: // before proceeding -- gives time after creating to initialize other Me@28: // stuff before the coreLoops set off. Me@28: _VMSMasterEnv->setupComplete = 0; Me@28: Me@28: //Make the threads that animate the core loops Me@28: for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ ) Me@53: { coreLoopThdParams[coreIdx] = VMS__malloc( sizeof(ThdParams) ); Me@28: coreLoopThdParams[coreIdx]->coreNum = coreIdx; Me@28: Me@28: retCode = Me@28: pthread_create( &(coreLoopThdHandles[coreIdx]), Me@28: thdAttrs, Me@28: &coreLoop, Me@28: (void *)(coreLoopThdParams[coreIdx]) ); Me@50: if(retCode){printf("ERROR creating thread: %d\n", retCode); exit(1);} Me@28: } Me@28: } Me@28: Me@0: /*Semantic layer calls this when it want the system to start running.. Me@0: * Me@24: *This starts the core loops running then waits for them to exit. Me@0: */ Me@12: void Me@24: VMS__start_the_work_then_wait_until_done() Me@12: { int coreIdx; Me@24: //Start the core loops running Me@25: Me@25: //tell the core loop threads that setup is complete Me@25: //get lock, to lock out any threads still starting up -- they'll see Me@25: // that setupComplete is true before entering while loop, and so never Me@25: // wait on the condition Me@26: pthread_mutex_lock( &suspendLock ); Me@25: _VMSMasterEnv->setupComplete = 1; Me@26: pthread_mutex_unlock( &suspendLock ); Me@26: pthread_cond_broadcast( &suspend_cond ); Me@25: Me@25: Me@24: //wait for all to complete Me@8: for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ ) Me@8: { Me@25: pthread_join( coreLoopThdHandles[coreIdx], NULL ); Me@24: } Me@25: Me@24: //NOTE: do not clean up VMS env here -- semantic layer has to have Me@24: // a chance to clean up its environment first, then do a call to free Me@24: // the Master env and rest of VMS locations Me@8: } Me@0: msach@70: #ifdef SEQUENTIAL Me@28: /*Only difference between version with an OS thread pinned to each core and Me@28: * the sequential version of VMS is VMS__init_Seq, this, and coreLoop_Seq. Me@28: */ Me@28: void Me@28: VMS__start_the_work_then_wait_until_done_Seq() Me@28: { Me@28: //Instead of un-suspending threads, just call the one and only Me@28: // core loop (sequential version), in the main thread. Me@28: coreLoop_Seq( NULL ); msach@75: flushRegisters(); Me@28: Me@28: } msach@70: #endif Me@28: Me@50: inline VirtProcr * Me@50: VMS__create_procr( VirtProcrFnPtr fnPtr, void *initialData ) Me@50: { VirtProcr *newPr; msach@76: void *stackLocs; Me@50: Me@50: newPr = VMS__malloc( sizeof(VirtProcr) ); Me@50: stackLocs = VMS__malloc( VIRT_PROCR_STACK_SIZE ); Me@50: if( stackLocs == 0 ) Me@50: { perror("VMS__malloc stack"); exit(1); } Me@50: msach@69: return create_procr_helper( newPr, fnPtr, initialData, stackLocs ); Me@50: } Me@50: Me@50: /* "ext" designates that it's for use outside the VMS system -- should only Me@50: * be called from main thread or other thread -- never from code animated by Me@50: * a VMS virtual processor. Me@50: */ Me@50: inline VirtProcr * Me@50: VMS_ext__create_procr( VirtProcrFnPtr fnPtr, void *initialData ) Me@50: { VirtProcr *newPr; Me@50: char *stackLocs; Me@50: Me@50: newPr = malloc( sizeof(VirtProcr) ); Me@50: stackLocs = malloc( VIRT_PROCR_STACK_SIZE ); Me@50: if( stackLocs == 0 ) Me@50: { perror("malloc stack"); exit(1); } Me@50: msach@69: return create_procr_helper( newPr, fnPtr, initialData, stackLocs ); Me@50: } Me@50: Me@8: Me@64: /*Anticipating multi-tasking Me@64: */ Me@64: void * Me@64: VMS__give_sem_env_for( VirtProcr *animPr ) Me@64: { Me@64: return _VMSMasterEnv->semanticEnv; Me@64: } Me@64: //=========================================================================== Me@26: /*there is a label inside this function -- save the addr of this label in Me@0: * the callingPr struc, as the pick-up point from which to start the next Me@0: * work-unit for that procr. If turns out have to save registers, then Me@0: * save them in the procr struc too. Then do assembly jump to the CoreLoop's Me@0: * "done with work-unit" label. The procr struc is in the request in the Me@0: * slave that animated the just-ended work-unit, so all the state is saved Me@0: * there, and will get passed along, inside the request handler, to the Me@0: * next work-unit for that procr. Me@0: */ Me@8: void Me@38: VMS__suspend_procr( VirtProcr *animatingPr ) Me@55: { Me@30: Me@30: //The request to master will cause this suspended virt procr to get Me@30: // scheduled again at some future point -- to resume, core loop jumps Me@30: // to the resume point (below), which causes restore of saved regs and Me@30: // "return" from this call. msach@71: //animatingPr->nextInstrPt = &&ResumePt; Me@30: Me@30: //return ownership of the virt procr and sched slot to Master virt pr Me@38: animatingPr->schedSlot->workIsDone = TRUE; Me@1: Me@41: //=========================== Measurement stuff ======================== Me@38: #ifdef MEAS__TIME_STAMP_SUSP Me@41: //record time stamp: compare to time-stamp recorded below Me@38: saveLowTimeStampCountInto( animatingPr->preSuspTSCLow ); Me@38: #endif Me@41: //======================================================================= Me@30: msach@71: switchToCoreLoop(animatingPr); msach@71: flushRegisters(); Me@55: Me@55: //======================================================================= Me@30: Me@38: #ifdef MEAS__TIME_STAMP_SUSP Me@41: //NOTE: only take low part of count -- do sanity check when take diff Me@38: saveLowTimeStampCountInto( animatingPr->postSuspTSCLow ); Me@38: #endif Me@30: Me@0: return; Me@0: } Me@0: Me@22: Me@22: Me@50: /*For this implementation of VMS, it may not make much sense to have the Me@50: * system of requests for creating a new processor done this way.. but over Me@50: * the scope of single-master, multi-master, mult-tasking, OS-implementing, Me@50: * distributed-memory, and so on, this gives VMS implementation a chance to Me@50: * do stuff before suspend, in the AppVP, and in the Master before the plugin Me@50: * is called, as well as in the lang-lib before this is called, and in the Me@50: * plugin. So, this gives both VMS and language implementations a chance to Me@50: * intercept at various points and do order-dependent stuff. Me@50: *Having a standard VMSNewPrReqData struc allows the language to create and Me@50: * free the struc, while VMS knows how to get the newPr if it wants it, and Me@50: * it lets the lang have lang-specific data related to creation transported Me@50: * to the plugin. Me@50: */ Me@50: void Me@50: VMS__send_create_procr_req( void *semReqData, VirtProcr *reqstingPr ) Me@50: { VMSReqst req; Me@50: Me@50: req.reqType = createReq; Me@50: req.semReqData = semReqData; Me@50: req.nextReqst = reqstingPr->requests; Me@50: reqstingPr->requests = &req; Me@50: Me@50: VMS__suspend_procr( reqstingPr ); Me@50: } Me@50: Me@22: Me@38: /* Me@22: *This adds a request to dissipate, then suspends the processor so that the Me@22: * request handler will receive the request. The request handler is what Me@22: * does the work of freeing memory and removing the processor from the Me@22: * semantic environment's data structures. Me@22: *The request handler also is what figures out when to shutdown the VMS Me@22: * system -- which causes all the core loop threads to die, and returns from Me@22: * the call that started up VMS to perform the work. Me@22: * Me@22: *This form is a bit misleading to understand if one is trying to figure out Me@22: * how VMS works -- it looks like a normal function call, but inside it Me@22: * sends a request to the request handler and suspends the processor, which Me@22: * jumps out of the VMS__dissipate_procr function, and out of all nestings Me@22: * above it, transferring the work of dissipating to the request handler, Me@22: * which then does the actual work -- causing the processor that animated Me@22: * the call of this function to disappear and the "hanging" state of this Me@22: * function to just poof into thin air -- the virtual processor's trace Me@22: * never returns from this call, but instead the virtual processor's trace Me@22: * gets suspended in this call and all the virt processor's state disap- Me@22: * pears -- making that suspend the last thing in the virt procr's trace. Me@8: */ Me@8: void Me@53: VMS__send_dissipate_req( VirtProcr *procrToDissipate ) Me@50: { VMSReqst req; Me@22: Me@50: req.reqType = dissipate; Me@50: req.nextReqst = procrToDissipate->requests; Me@50: procrToDissipate->requests = &req; Me@50: Me@22: VMS__suspend_procr( procrToDissipate ); Me@50: } Me@50: Me@50: Me@50: /* "ext" designates that it's for use outside the VMS system -- should only Me@50: * be called from main thread or other thread -- never from code animated by Me@50: * a VMS virtual processor. Me@50: * Me@50: *Use this version to dissipate VPs created outside the VMS system. Me@50: */ Me@50: void Me@50: VMS_ext__dissipate_procr( VirtProcr *procrToDissipate ) Me@50: { Me@50: //NOTE: initialData was given to the processor, so should either have Me@50: // been alloc'd with VMS__malloc, or freed by the level above animPr. Me@50: //So, all that's left to free here is the stack and the VirtProcr struc Me@50: // itself Me@50: //Note, should not stack-allocate initial data -- no guarantee, in Me@50: // general that creating processor will outlive ones it creates. Me@50: free( procrToDissipate->startOfStack ); Me@50: free( procrToDissipate ); Me@50: } Me@50: Me@22: Me@22: Me@53: /*This call's name indicates that request is malloc'd -- so req handler Me@53: * has to free any extra requests tacked on before a send, using this. Me@53: * Me@53: * This inserts the semantic-layer's request data into standard VMS carrier Me@53: * request data-struct that is mallocd. The sem request doesn't need to Me@53: * be malloc'd if this is called inside the same call chain before the Me@53: * send of the last request is called. Me@53: * Me@53: *The request handler has to call VMS__free_VMSReq for any of these Me@22: */ Me@22: inline void Me@53: VMS__add_sem_request_in_mallocd_VMSReqst( void *semReqData, Me@53: VirtProcr *callingPr ) Me@53: { VMSReqst *req; Me@22: Me@53: req = VMS__malloc( sizeof(VMSReqst) ); Me@53: req->reqType = semantic; Me@53: req->semReqData = semReqData; Me@53: req->nextReqst = callingPr->requests; Me@53: callingPr->requests = req; Me@22: } Me@22: Me@50: /*This inserts the semantic-layer's request data into standard VMS carrier Me@50: * request data-struct is allocated on stack of this call & ptr to it sent Me@50: * to plugin Me@50: *Then it does suspend, to cause request to be sent. Me@50: */ Me@50: inline void Me@50: VMS__send_sem_request( void *semReqData, VirtProcr *callingPr ) Me@50: { VMSReqst req; Me@22: Me@50: req.reqType = semantic; Me@50: req.semReqData = semReqData; Me@50: req.nextReqst = callingPr->requests; Me@50: callingPr->requests = &req; Me@50: Me@50: VMS__suspend_procr( callingPr ); Me@50: } Me@50: Me@50: Me@50: inline void Me@50: VMS__send_VMSSem_request( void *semReqData, VirtProcr *callingPr ) Me@50: { VMSReqst req; Me@50: Me@50: req.reqType = VMSSemantic; Me@50: req.semReqData = semReqData; Me@50: req.nextReqst = callingPr->requests; //gab any other preceeding Me@50: callingPr->requests = &req; Me@50: Me@50: VMS__suspend_procr( callingPr ); Me@50: } Me@50: Me@50: Me@50: /* Me@38: */ Me@24: VMSReqst * Me@50: VMS__take_next_request_out_of( VirtProcr *procrWithReq ) Me@31: { VMSReqst *req; Me@31: Me@31: req = procrWithReq->requests; Me@38: if( req == NULL ) return NULL; Me@31: Me@31: procrWithReq->requests = procrWithReq->requests->nextReqst; Me@50: return req; Me@24: } Me@22: Me@24: Me@24: inline void * Me@24: VMS__take_sem_reqst_from( VMSReqst *req ) Me@24: { Me@24: return req->semReqData; Me@24: } Me@24: Me@24: Me@24: Me@50: /* This is for OS requests and VMS infrastructure requests, such as to create Me@50: * a probe -- a probe is inside the heart of VMS-core, it's not part of any Me@50: * language -- but it's also a semantic thing that's triggered from and used Me@50: * in the application.. so it crosses abstractions.. so, need some special Me@50: * pattern here for handling such requests. Me@52: * Doing this just like it were a second language sharing VMS-core. Me@52: * Me@50: * This is called from the language's request handler when it sees a request Me@50: * of type VMSSemReq Me@52: * Me@52: * TODO: Later change this, to give probes their own separate plugin & have Me@52: * VMS-core steer the request to appropriate plugin Me@52: * Do the same for OS calls -- look later at it.. Me@50: */ Me@50: void inline Me@50: VMS__handle_VMSSemReq( VMSReqst *req, VirtProcr *requestingPr, void *semEnv, Me@50: ResumePrFnPtr resumePrFnPtr ) Me@50: { VMSSemReq *semReq; Me@50: IntervalProbe *newProbe; Me@24: Me@50: semReq = req->semReqData; Me@24: Me@50: newProbe = VMS__malloc( sizeof(IntervalProbe) ); Me@65: newProbe->nameStr = VMS__strDup( semReq->nameStr ); Me@50: newProbe->hist = NULL; Me@50: newProbe->schedChoiceWasRecorded = FALSE; Me@53: Me@53: //This runs in masterVP, so no race-condition worries Me@50: newProbe->probeID = Me@50: addToDynArray( newProbe, _VMSMasterEnv->dynIntervalProbesInfo ); Me@50: Me@53: requestingPr->dataRetFromReq = newProbe; Me@50: Me@50: (*resumePrFnPtr)( requestingPr, semEnv ); Me@22: } Me@22: Me@22: Me@22: Me@24: /*This must be called by the request handler plugin -- it cannot be called Me@24: * from the semantic library "dissipate processor" function -- instead, the Me@50: * semantic layer has to generate a request, and the plug-in calls this Me@24: * function. Me@24: *The reason is that this frees the virtual processor's stack -- which is Me@24: * still in use inside semantic library calls! Me@24: * Me@24: *This frees or recycles all the state owned by and comprising the VMS Me@24: * portion of the animating virtual procr. The request handler must first Me@24: * free any semantic data created for the processor that didn't use the Me@24: * VMS_malloc mechanism. Then it calls this, which first asks the malloc Me@24: * system to disown any state that did use VMS_malloc, and then frees the Me@24: * statck and the processor-struct itself. Me@24: *If the dissipated processor is the sole (remaining) owner of VMS__malloc'd Me@24: * state, then that state gets freed (or sent to recycling) as a side-effect Me@24: * of dis-owning it. Me@24: */ Me@24: void Me@53: VMS__dissipate_procr( VirtProcr *animatingPr ) Me@24: { Me@24: //dis-own all locations owned by this processor, causing to be freed Me@24: // any locations that it is (was) sole owner of Me@29: //TODO: implement VMS__malloc system, including "give up ownership" Me@24: Me@24: Me@24: //NOTE: initialData was given to the processor, so should either have Me@24: // been alloc'd with VMS__malloc, or freed by the level above animPr. Me@24: //So, all that's left to free here is the stack and the VirtProcr struc Me@24: // itself Me@50: //Note, should not stack-allocate initial data -- no guarantee, in Me@50: // general that creating processor will outlive ones it creates. Me@50: VMS__free( animatingPr->startOfStack ); Me@50: VMS__free( animatingPr ); Me@24: } Me@24: Me@24: Me@53: //TODO: look at architecting cleanest separation between request handler Me@29: // and master loop, for dissipate, create, shutdown, and other non-semantic Me@29: // requests. Issue is chain: one removes requests from AppVP, one dispatches Me@29: // on type of request, and one handles each type.. but some types require Me@29: // action from both request handler and master loop -- maybe just give the Me@29: // request handler calls like: VMS__handle_X_request_type Me@24: Me@29: Me@29: /*This is called by the semantic layer's request handler when it decides its Me@29: * time to shut down the VMS system. Calling this causes the core loop OS Me@29: * threads to exit, which unblocks the entry-point function that started up Me@29: * VMS, and allows it to grab the result and return to the original single- Me@29: * threaded application. Me@22: * Me@29: *The _VMSMasterEnv is needed by this shut down function, so the create-seed- Me@29: * and-wait function has to free a bunch of stuff after it detects the Me@29: * threads have all died: the masterEnv, the thread-related locations, Me@29: * masterVP any AppVPs that might still be allocated and sitting in the Me@29: * semantic environment, or have been orphaned in the _VMSWorkQ. Me@29: * Me@53: *NOTE: the semantic plug-in is expected to use VMS__malloc to get all the Me@29: * locations it needs, and give ownership to masterVP. Then, they will be Me@53: * automatically freed. Me@22: * Me@29: *In here,create one core-loop shut-down processor for each core loop and put Me@31: * them all directly into the readyToAnimateQ. Me@29: *Note, this function can ONLY be called after the semantic environment no Me@29: * longer cares if AppVPs get animated after the point this is called. In Me@29: * other words, this can be used as an abort, or else it should only be Me@29: * called when all AppVPs have finished dissipate requests -- only at that Me@29: * point is it sure that all results have completed. Me@22: */ Me@22: void Me@53: VMS__shutdown() Me@8: { int coreIdx; Me@14: VirtProcr *shutDownPr; Me@22: Me@29: //create the shutdown processors, one for each core loop -- put them Me@31: // directly into the Q -- each core will die when gets one Me@8: for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ ) Me@50: { //Note, this is running in the master Me@29: shutDownPr = VMS__create_procr( &endOSThreadFn, NULL ); Me@55: writeVMSQ( shutDownPr, _VMSMasterEnv->readyToAnimateQs[coreIdx] ); Me@8: } Me@22: Me@12: } Me@12: Me@12: Me@29: /*Am trying to be cute, avoiding IF statement in coreLoop that checks for Me@29: * a special shutdown procr. Ended up with extra-complex shutdown sequence. Me@29: *This function has the sole purpose of setting the stack and framePtr Me@29: * to the coreLoop's stack and framePtr.. it does that then jumps to the Me@29: * core loop's shutdown point -- might be able to just call Pthread_exit Me@30: * from here, but am going back to the pthread's stack and setting everything Me@29: * up just as if it never jumped out, before calling pthread_exit. Me@29: *The end-point of core loop will free the stack and so forth of the Me@29: * processor that animates this function, (this fn is transfering the Me@29: * animator of the AppVP that is in turn animating this function over Me@29: * to core loop function -- note that this slices out a level of virtual Me@29: * processors). Me@29: */ Me@29: void Me@29: endOSThreadFn( void *initData, VirtProcr *animatingPr ) msach@71: { msach@75: #ifdef SEQUENTIAL msach@75: asmTerminateCoreLoopSeq(animatingPr); msach@75: #else msach@71: asmTerminateCoreLoop(animatingPr); msach@75: #endif Me@30: } Me@29: Me@29: Me@53: /*This is called from the startup & shutdown Me@24: */ Me@24: void Me@53: VMS__cleanup_at_end_of_shutdown() Me@31: { msach@78: //unused msach@78: //VMSQueueStruc **readyToAnimateQs; msach@78: //int coreIdx; msach@78: //VirtProcr **masterVPs; msach@78: //SchedSlot ***allSchedSlots; //ptr to array of ptrs Me@31: Me@65: //Before getting rid of everything, print out any measurements made msach@69: forAllInDynArrayDo( _VMSMasterEnv->measHistsInfo, (DynArrayFnPtr)&printHist ); msach@78: forAllInDynArrayDo( _VMSMasterEnv->measHistsInfo, (DynArrayFnPtr)&saveHistToFile); msach@143: forAllInDynArrayDo( _VMSMasterEnv->measHistsInfo, &freeHist ); Me@65: #ifdef MEAS__TIME_PLUGIN Me@68: printHist( _VMSMasterEnv->reqHdlrLowTimeHist ); msach@84: saveHistToFile( _VMSMasterEnv->reqHdlrLowTimeHist ); Me@68: printHist( _VMSMasterEnv->reqHdlrHighTimeHist ); msach@79: saveHistToFile( _VMSMasterEnv->reqHdlrHighTimeHist ); Me@68: freeHistExt( _VMSMasterEnv->reqHdlrLowTimeHist ); Me@68: freeHistExt( _VMSMasterEnv->reqHdlrHighTimeHist ); Me@65: #endif Me@65: #ifdef MEAS__TIME_MALLOC Me@65: printHist( _VMSMasterEnv->mallocTimeHist ); msach@79: saveHistToFile( _VMSMasterEnv->mallocTimeHist ); Me@65: printHist( _VMSMasterEnv->freeTimeHist ); msach@79: saveHistToFile( _VMSMasterEnv->freeTimeHist ); Me@65: freeHistExt( _VMSMasterEnv->mallocTimeHist ); Me@65: freeHistExt( _VMSMasterEnv->freeTimeHist ); Me@65: #endif Me@65: #ifdef MEAS__TIME_MASTER_LOCK Me@65: printHist( _VMSMasterEnv->masterLockLowTimeHist ); Me@65: printHist( _VMSMasterEnv->masterLockHighTimeHist ); Me@65: #endif Me@65: #ifdef MEAS__TIME_MASTER Me@65: printHist( _VMSMasterEnv->pluginTimeHist ); Me@65: for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) Me@65: { Me@65: freeVMSQ( readyToAnimateQs[ coreIdx ] ); Me@65: //master VPs were created external to VMS, so use external free Me@65: VMS__dissipate_procr( masterVPs[ coreIdx ] ); Me@65: Me@65: freeSchedSlots( allSchedSlots[ coreIdx ] ); Me@65: } Me@65: #endif Me@65: #ifdef MEAS__TIME_STAMP_SUSP Me@65: printHist( _VMSMasterEnv->pluginTimeHist ); Me@65: for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) Me@65: { Me@65: freeVMSQ( readyToAnimateQs[ coreIdx ] ); Me@65: //master VPs were created external to VMS, so use external free Me@65: VMS__dissipate_procr( masterVPs[ coreIdx ] ); Me@65: Me@65: freeSchedSlots( allSchedSlots[ coreIdx ] ); Me@65: } Me@65: #endif Me@65: Me@53: //All the environment data has been allocated with VMS__malloc, so just Me@53: // free its internal big-chunk and all inside it disappear. Me@53: /* Me@31: readyToAnimateQs = _VMSMasterEnv->readyToAnimateQs; Me@31: masterVPs = _VMSMasterEnv->masterVPs; Me@31: allSchedSlots = _VMSMasterEnv->allSchedSlots; Me@31: Me@31: for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) Me@24: { Me@55: freeVMSQ( readyToAnimateQs[ coreIdx ] ); Me@50: //master VPs were created external to VMS, so use external free Me@53: VMS__dissipate_procr( masterVPs[ coreIdx ] ); Me@31: Me@31: freeSchedSlots( allSchedSlots[ coreIdx ] ); Me@24: } Me@31: Me@53: VMS__free( _VMSMasterEnv->readyToAnimateQs ); Me@53: VMS__free( _VMSMasterEnv->masterVPs ); Me@53: VMS__free( _VMSMasterEnv->allSchedSlots ); Me@50: Me@50: //============================= MEASUREMENT STUFF ======================== Me@50: #ifdef STATS__TURN_ON_PROBES Me@53: freeDynArrayDeep( _VMSMasterEnv->dynIntervalProbesInfo, &VMS__free_probe); Me@50: #endif Me@50: //======================================================================== Me@53: */ Me@53: //These are the only two that use system free Me@53: VMS_ext__free_free_list( _VMSMasterEnv->freeListHead ); Me@53: free( (void *)_VMSMasterEnv ); Me@24: } Me@24: Me@54: Me@54: //================================ Me@54: Me@54: Me@54: /*Later, improve this -- for now, just exits the application after printing Me@54: * the error message. Me@54: */ Me@54: void Me@54: VMS__throw_exception( char *msgStr, VirtProcr *reqstPr, VMSExcp *excpData ) Me@54: { msach@69: printf("%s",msgStr); Me@54: fflush(stdin); Me@54: exit(1); Me@54: } Me@54: