Me@0: /* Me@0: * Copyright 2010 OpenSourceCodeStewardshipFoundation Me@0: * Me@0: * Licensed under BSD Me@0: */ Me@0: Me@0: #include Me@0: #include Me@0: #include Me@0: Me@0: #include "VMS.h" Me@0: #include "Queue_impl/BlockingQueue.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@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: 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: Me@28: void Me@28: create_masterEnv() Me@31: { MasterEnv *masterEnv; Me@31: SRSWQueueStruc **readyToAnimateQs; Me@31: int coreIdx; Me@31: VirtProcr **masterVPs; Me@31: SchedSlot ***allSchedSlots; //ptr to array of ptrs Me@31: Me@31: //Make the master env, which holds everything else Me@1: _VMSMasterEnv = malloc( sizeof(MasterEnv) ); Me@1: masterEnv = _VMSMasterEnv; Me@31: //Need to set start pt here 'cause used by seed procr, which is created Me@31: // before the first core loop starts up. -- not sure how yet.. Me@31: // masterEnv->coreLoopStartPt = ; Me@31: // masterEnv->coreLoopEndPt = ; Me@31: Me@31: //Make a readyToAnimateQ for each core loop Me@31: readyToAnimateQs = malloc( NUM_CORES * sizeof(SRSWQueueStruc *) ); Me@31: masterVPs = malloc( NUM_CORES * sizeof(VirtProcr *) ); Me@0: Me@31: //One array for each core, 3 in array, core's masterVP scheds all Me@31: allSchedSlots = malloc( NUM_CORES * sizeof(SchedSlot *) ); Me@0: Me@31: for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) Me@31: { Me@31: readyToAnimateQs[ coreIdx ] = makeSRSWQ(); Me@31: Me@31: //Q: should give masterVP core-specific into as its init data? Me@31: masterVPs[ coreIdx ] = VMS__create_procr( &masterLoop, masterEnv ); Me@31: masterVPs[ coreIdx ]->coreAnimatedBy = coreIdx; Me@31: allSchedSlots[ coreIdx ] = create_sched_slots(); //makes for one core Me@31: } Me@31: _VMSMasterEnv->readyToAnimateQs = readyToAnimateQs; Me@31: _VMSMasterEnv->masterVPs = masterVPs; Me@31: _VMSMasterEnv->allSchedSlots = allSchedSlots; Me@0: 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@30: //==================== malloc substitute ======================== Me@30: // Me@30: //Testing whether malloc is using thread-local storage and therefore Me@30: // causing unreliable behavior. Me@30: //Just allocate a massive chunk of memory and roll own malloc/free and Me@30: // make app use VMS__malloc_to, which will suspend and perform malloc Me@30: // in the master, taking from this massive chunk. Me@30: Me@30: // initFreeList(); Me@0: } Me@0: Me@30: /* Me@30: void Me@30: initMasterMalloc() Me@30: { Me@30: _VMSMasterEnv->mallocChunk = malloc( MASSIVE_MALLOC_SIZE ); Me@30: Me@30: //The free-list element is the first several locations of an Me@30: // allocated chunk -- the address given to the application is pre- Me@30: // pended with both the ownership structure and the free-list struc. Me@30: //So, write the values of these into the first locations of Me@30: // mallocChunk -- which marks it as free & puts in its size. Me@30: listElem = (FreeListElem *)_VMSMasterEnv->mallocChunk; Me@30: listElem->size = MASSIVE_MALLOC_SIZE - NUM_PREPEND_BYTES Me@30: listElem->next = NULL; Me@30: } Me@30: Me@30: void Me@30: dissipateMasterMalloc() Me@30: { Me@30: //Just foo code -- to get going -- doing as if free list were link-list Me@30: currElem = _VMSMasterEnv->freeList; Me@30: while( currElem != NULL ) Me@30: { Me@30: nextElem = currElem->next; Me@30: masterFree( currElem ); Me@30: currElem = nextElem; Me@30: } Me@30: free( _VMSMasterEnv->freeList ); Me@30: } Me@30: */ Me@30: Me@31: SchedSlot ** Me@31: create_sched_slots() Me@31: { SchedSlot **schedSlots; Me@0: int i; Me@0: Me@8: schedSlots = malloc( NUM_SCHED_SLOTS * sizeof(SchedSlot *) ); Me@8: Me@1: for( i = 0; i < NUM_SCHED_SLOTS; i++ ) Me@0: { Me@8: schedSlots[i] = 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@31: free( schedSlots[i] ); Me@31: } Me@31: 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@28: { coreLoopThdParams[coreIdx] = 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@28: if(retCode){printf("ERROR creating thread: %d\n", retCode); exit(0);} 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@24: //=========================================================================== Me@25: TSCount startCount, endCount; Me@24: unsigned long long count = 0, freq = 0; Me@25: double runTime; Me@0: Me@25: startCount = getTSCount(); 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@24: Me@24: Me@25: endCount = getTSCount(); Me@25: count = endCount - startCount; Me@24: Me@25: runTime = (double)count / (double)TSCOUNT_FREQ; Me@25: Me@25: printf("\n Time startup to shutdown: %f\n", runTime); fflush( stdin ); Me@8: } Me@0: 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 ); Me@28: Me@28: } Me@28: Me@0: Me@0: Me@8: /*Create stack, then create __cdecl structure on it and put initialData and Me@8: * pointer to the new structure instance into the parameter positions on Me@8: * the stack Me@8: *Then put function pointer into nextInstrPt -- the stack is setup in std Me@8: * call structure, so jumping to function ptr is same as a GCC generated Me@8: * function call Me@8: *No need to save registers on old stack frame, because there's no old Me@8: * animator state to return to -- Me@8: * Me@8: */ Me@8: VirtProcr * Me@8: VMS__create_procr( VirtProcrFnPtr fnPtr, void *initialData ) Me@8: { VirtProcr *newPr; Me@8: char *stackLocs, *stackPtr; Me@8: Me@8: newPr = malloc( sizeof(VirtProcr) ); Me@12: newPr->procrID = numProcrsCreated++; Me@8: newPr->nextInstrPt = fnPtr; Me@8: newPr->initialData = initialData; Me@31: newPr->requests = NULL; Me@31: // newPr->coreLoopStartPt = _VMSMasterEnv->coreLoopStartPt; Me@8: Me@14: //fnPtr takes two params -- void *initData & void *animProcr Me@8: //alloc stack locations, make stackPtr be the highest addr minus room Me@14: // for 2 params + return addr. Return addr (NULL) is in loc pointed to Me@14: // by stackPtr, initData at stackPtr + 4 bytes, animatingPr just above Me@22: stackLocs = malloc( VIRT_PROCR_STACK_SIZE ); Me@26: if(stackLocs == 0) Me@26: {perror("malloc stack"); exit(1);} Me@22: newPr->startOfStack = stackLocs; Me@22: stackPtr = ( (char *)stackLocs + VIRT_PROCR_STACK_SIZE - 0x10 ); Me@8: //setup __cdecl on stack -- coreloop will switch to stackPtr before jmp Me@22: *( (int *)stackPtr + 2 ) = (int) newPr; //rightmost param -- 32bit pointer Me@14: *( (int *)stackPtr + 1 ) = (int) initialData; //next param to left Me@8: newPr->stackPtr = stackPtr; //core loop will switch to this, then Me@8: newPr->framePtr = stackPtr; //suspend loop will save new stack & frame ptr Me@8: Me@8: return newPr; Me@8: } Me@8: Me@8: 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@22: VMS__suspend_procr( VirtProcr *callingPr ) Me@14: { void *jmpPt, *stackPtrAddr, *framePtrAddr, *coreLoopStackPtr; Me@14: void *coreLoopFramePtr; Me@0: Me@14: //The request to master will cause this suspended virt procr to get Me@14: // scheduled again at some future point -- to resume, core loop jumps Me@14: // to the resume point (below), which causes restore of saved regs and Me@14: // "return" from this call. Me@1: callingPr->nextInstrPt = &&ResumePt; Me@1: Me@1: //return ownership of the virt procr and sched slot to Master virt pr Me@1: callingPr->schedSlot->workIsDone = TRUE; Me@14: // coreIdx = callingPr->coreAnimatedBy; Me@1: Me@18: stackPtrAddr = &(callingPr->stackPtr); Me@18: framePtrAddr = &(callingPr->framePtr); Me@26: Me@31: jmpPt = _VMSMasterEnv->coreLoopStartPt; Me@14: coreLoopFramePtr = callingPr->coreLoopFramePtr;//need this only Me@18: coreLoopStackPtr = callingPr->coreLoopStackPtr;//shouldn't need -- safety Me@1: Me@26: //Eclipse's compilation sequence complains -- so break into two Me@26: // separate in-line assembly pieces Me@26: //Save the virt procr's stack and frame ptrs, Me@18: asm volatile("movl %0, %%eax; \ Me@18: movl %%esp, (%%eax); \ Me@18: movl %1, %%eax; \ Me@26: movl %%ebp, (%%eax) "\ Me@26: /* outputs */ : "=g" (stackPtrAddr), "=g" (framePtrAddr) \ Me@26: /* inputs */ : \ Me@26: /* clobber */ : "%eax" \ Me@26: ); Me@26: Me@26: //restore coreloop's frame ptr, then jump back to "start" of core loop Me@26: //Note, GCC compiles to assembly that saves esp and ebp in the stack Me@26: // frame -- so have to explicitly do assembly that saves to memory Me@26: asm volatile("movl %0, %%eax; \ Me@26: movl %1, %%esp; \ Me@26: movl %2, %%ebp; \ Me@18: jmp %%eax " \ Me@26: /* outputs */ : \ Me@26: /* inputs */ : "m" (jmpPt), "m"(coreLoopStackPtr), "m"(coreLoopFramePtr)\ Me@18: /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx", "%edi","%esi" \ Me@12: ); //list everything as clobbered to force GCC to save all Me@12: // live vars that are in regs on stack before this Me@12: // assembly, so that stack pointer is correct, before jmp Me@1: Me@1: ResumePt: Me@0: return; Me@0: } Me@0: Me@22: Me@22: Me@22: Me@22: /*Not sure yet the form going to put "dissipate" in, so this is the third Me@22: * possibility -- the semantic layer can just make a macro that looks like Me@22: * a call to its name, then expands to a call to this. Me@8: * Me@22: *As of June 30, 2010 this looks like the top choice.. Me@8: * 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@22: VMS__dissipate_procr( VirtProcr *procrToDissipate ) Me@22: { VMSReqst *req; Me@22: Me@22: req = malloc( sizeof(VMSReqst) ); Me@22: // req->virtProcrFrom = callingPr; Me@22: req->reqType = dissipate; Me@22: req->nextReqst = procrToDissipate->requests; Me@22: procrToDissipate->requests = req; Me@22: Me@22: VMS__suspend_procr( procrToDissipate ); Me@22: } Me@22: Me@22: Me@22: /*This inserts the semantic-layer's request data into standard VMS carrier Me@22: */ Me@22: inline void Me@24: VMS__add_sem_request( void *semReqData, VirtProcr *callingPr ) Me@22: { VMSReqst *req; Me@22: Me@22: req = malloc( sizeof(VMSReqst) ); Me@22: // req->virtProcrFrom = callingPr; Me@22: req->reqType = semantic; Me@22: req->semReqData = semReqData; Me@22: req->nextReqst = callingPr->requests; Me@22: callingPr->requests = req; Me@22: } Me@22: Me@22: Me@22: Me@22: //TODO: add a semantic-layer supplied "freer" for the semantic-data portion Me@22: // of a request -- IE call with both a virt procr and a fn-ptr to request Me@22: // freer (or maybe put request freer as a field in virt procr?) Me@22: void Me@22: VMS__remove_and_free_top_request( VirtProcr *procrWithReq ) Me@22: { VMSReqst *req; Me@22: Me@22: req = procrWithReq->requests; Me@29: if( req == NULL ) return; Me@22: procrWithReq->requests = procrWithReq->requests->nextReqst; Me@29: VMS__free_request( req ); Me@22: } Me@22: Me@24: Me@24: //TODO: add a semantic-layer supplied "freer" for the semantic-data portion Me@24: // of a request -- IE call with both a virt procr and a fn-ptr to request Me@24: // freer (also maybe put sem request freer as a field in virt procr?) Me@31: //SSR relies right now on this only freeing VMS layer of request -- the Me@26: // semantic portion of request is alloc'd and freed by request handler Me@22: void Me@24: VMS__free_request( VMSReqst *req ) Me@24: { Me@24: free( req ); Me@24: } Me@24: Me@24: VMSReqst * Me@24: VMS__take_top_request_from( VirtProcr *procrWithReq ) Me@24: { VMSReqst *req; Me@24: Me@24: req = procrWithReq->requests; Me@24: if( req == NULL ) return req; Me@31: Me@24: procrWithReq->requests = procrWithReq->requests->nextReqst; Me@24: return req; Me@24: } Me@24: Me@31: VMSReqst * Me@31: VMS__free_top_and_give_next_request_from( VirtProcr *procrWithReq ) Me@31: { VMSReqst *req; Me@31: Me@31: req = procrWithReq->requests; Me@31: if( req == NULL ) return req; Me@31: Me@31: procrWithReq->requests = procrWithReq->requests->nextReqst; Me@31: VMS__free_request( req ); Me@31: return procrWithReq->requests; Me@31: } Me@31: Me@24: inline int Me@24: VMS__isSemanticReqst( VMSReqst *req ) Me@22: { Me@24: return ( req->reqType == semantic ); 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: inline int Me@24: VMS__isDissipateReqst( VMSReqst *req ) Me@24: { Me@24: return ( req->reqType == dissipate ); Me@24: } Me@24: Me@24: inline int Me@24: VMS__isCreateReqst( VMSReqst *req ) Me@24: { Me@24: return ( req->reqType == regCreated ); Me@24: } Me@24: Me@24: void Me@24: VMS__send_register_new_procr_request(VirtProcr *newPr, VirtProcr *reqstingPr) Me@24: { VMSReqst *req; Me@24: Me@24: req = malloc( sizeof(VMSReqst) ); Me@24: req->reqType = regCreated; Me@24: req->semReqData = newPr; Me@24: req->nextReqst = reqstingPr->requests; Me@24: reqstingPr->requests = req; Me@24: Me@24: VMS__suspend_procr( reqstingPr ); 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@24: * semantic layer has to generate a request for the plug-in to call 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@29: VMS__handle_dissipate_reqst( 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: //The dissipate request might still be attached, so remove and free it Me@24: VMS__remove_and_free_top_request( animatingPr ); 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@24: free( animatingPr->startOfStack ); Me@24: free( animatingPr ); Me@24: } Me@24: Me@24: Me@29: //TODO: re-architect so that have clean 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: void Me@29: endOSThreadFn( void *initData, VirtProcr *animatingPr ); 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@29: *NOTE: the semantic plug-in is expected to use VMS__malloc_to to get all the Me@29: * locations it needs, and give ownership to masterVP. Then, they will be Me@29: * automatically freed when the masterVP is dissipated. (This happens after Me@29: * the core loop threads have all exited) 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@29: VMS__handle_shutdown_reqst( void *dummy, VirtProcr *animatingPr ) 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@8: { Me@29: shutDownPr = VMS__create_procr( &endOSThreadFn, NULL ); Me@31: writeSRSWQ( 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 ) Me@29: { void *jmpPt, *coreLoopStackPtr, *coreLoopFramePtr; Me@29: Me@29: jmpPt = _VMSMasterEnv->coreLoopEndPt; Me@29: coreLoopStackPtr = animatingPr->coreLoopStackPtr; Me@29: coreLoopFramePtr = animatingPr->coreLoopFramePtr; Me@29: Me@29: Me@29: asm volatile("movl %0, %%eax; \ Me@29: movl %1, %%esp; \ Me@29: movl %2, %%ebp; \ Me@29: jmp %%eax " \ Me@29: /* outputs */ : \ Me@29: /* inputs */ : "m" (jmpPt), "m"(coreLoopStackPtr), "m"(coreLoopFramePtr)\ Me@29: /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx", "%edi","%esi" \ Me@29: ); Me@29: } Me@29: Me@29: Me@31: /*This is called after the threads have shut down and control has returned Me@30: * to the semantic layer, in the entry point function in the main thread. Me@30: * It has to free anything allocated during VMS_init, and any other alloc'd Me@24: * locations that might be left over. Me@24: */ Me@24: void Me@29: VMS__cleanup_after_shutdown() Me@31: { Me@31: SRSWQueueStruc **readyToAnimateQs; Me@31: int coreIdx; Me@31: VirtProcr **masterVPs; Me@31: SchedSlot ***allSchedSlots; //ptr to array of ptrs Me@31: 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@31: freeSRSWQ( readyToAnimateQs[ coreIdx ] ); Me@31: Me@31: VMS__handle_dissipate_reqst( masterVPs[ coreIdx ] ); Me@31: Me@31: freeSchedSlots( allSchedSlots[ coreIdx ] ); Me@24: } Me@31: Me@31: free( _VMSMasterEnv->readyToAnimateQs ); Me@31: free( _VMSMasterEnv->masterVPs ); Me@31: free( _VMSMasterEnv->allSchedSlots ); Me@24: Me@24: free( _VMSMasterEnv ); Me@24: } Me@24: Me@24: Me@24: //=========================================================================== Me@12: Me@12: inline TSCount getTSCount() Me@12: { unsigned int low, high; Me@12: TSCount out; Me@12: Me@12: saveTimeStampCountInto( low, high ); Me@12: out = high; Me@12: out = (out << 32) + low; Me@12: return out; Me@12: } Me@12: