Me@0: /* Me@0: * Copyright 2010 OpenSourceCodeStewardshipFoundation Me@0: * Me@0: * Licensed under BSD Me@0: */ Me@0: Me@0: Me@0: Me@9: #include Me@0: #include Me@0: #include Me@9: #include Me@0: Me@0: #include "VMS.h" Me@0: Me@0: Me@0: Me@0: /*This code is animated by the virtual Master processor. Me@0: * Me@11: *Polls each sched slot exactly once, hands any requests made by a newly Me@11: * done slave to the "request handler" plug-in function Me@0: * Me@11: *Any slots that need a virt procr assigned are given to the "schedule" Me@11: * plug-in function, which tries to assign a virt procr (slave) to it. Me@0: * Me@11: *When all slots needing a processor have been given to the schedule plug-in, Me@11: * a fraction of the procrs successfully scheduled are put into the Me@11: * work queue, then a continuation of this function is put in, then the rest Me@11: * of the virt procrs that were successfully scheduled. Me@0: * Me@11: *The first thing the continuation does is busy-wait until the previous Me@11: * animation completes. This is because an (unlikely) continuation may Me@11: * sneak through queue before previous continuation is done putting second Me@11: * part of scheduled slaves in, which is the only race condition. Me@0: * Me@0: */ Me@0: Me@4: /*May 29, 2010 -- birth a Master during init so that first core loop to Me@11: * start running gets it and does all the stuff for a newly born -- Me@11: * from then on, will be doing continuation, but do suspension self Me@4: * directly at end of master loop Me@4: *So VMS__init just births the master virtual processor same way it births Me@4: * all the others -- then does any extra setup needed and puts it into the Me@4: * work queue. Me@4: *However means have to make masterEnv a global static volatile the same way Me@4: * did with workQ in core loop. -- for performance, put the Me@11: * jump to the core loop directly in here, and have it directly jump back. Me@4: */ Me@4: void masterLoop( void *initData, VirtProcr *masterPr ) Me@11: { bool8 retCode; Me@4: int slotIdx, numScheduled, numInFirstChunk, filledSlotIdx; Me@4: SchedSlot *currSlot, **schedSlots, **filledSlots; Me@0: MasterEnv *masterEnv; Me@11: CASQueueStruc *workQ; Me@4: void *jmpPt; Me@4: Me@0: SlaveScheduler slaveScheduler; Me@0: RequestHandler requestHandler; Me@0: Me@4: //this will run as the first virt processor in workQ, and will be a Me@4: // new born -- so will do all the GCC-generated allocating space on Me@4: // the stack owned by master virt procr -- and will run this last bit Me@4: // of setup code.. Me@4: masterPr->nextInstrPt = &&masterLoopStartPt; Me@0: Me@4: Me@4: masterLoopStartPt: Me@0: Me@4: //if another reference to same Master VirtProcr still going, busy-wait Me@4: //Could put this lower, but don't want to think about shared stack.. Me@11: masterEnv = _VMSMasterEnv; Me@4: while( masterEnv->stillRunning ) /*busy wait*/ ; Me@4: //TODO: want to do busy-wait as assembly, to be sure stack not touched? Me@4: Me@4: //this is the only master running now, set flag again Me@4: masterEnv->stillRunning = TRUE; Me@4: Me@4: //TODO: gdb -- check that a volatile _VMSMasterEnv and _VMSWorkQ means Me@4: // all these will be re-filled every time jump here.. Me@4: workQ = _VMSWorkQ; Me@0: requestHandler = masterEnv->requestHandler; Me@0: slaveScheduler = masterEnv->slaveScheduler; Me@4: schedSlots = masterEnv->schedSlots; Me@4: filledSlots = masterEnv->filledSlots; Me@11: masterPr = masterEnv->masterVirtPr; //post-jmp clobbered, re-load Me@0: Me@0: Me@0: //prepare for scheduling Me@4: masterEnv->numFilled = 0; Me@0: Me@4: //Poll each slot's Done flag -- slot 0 reseved for master, start at 1 Me@4: for( slotIdx = 1; slotIdx < NUM_SCHED_SLOTS; slotIdx++) Me@0: { Me@4: currSlot = schedSlots[ slotIdx ]; Me@0: Me@4: if( currSlot->workIsDone ) Me@0: { Me@4: currSlot->workIsDone = FALSE; Me@4: currSlot->needsProcrAssigned = TRUE; Me@0: Me@0: //process requests from slave to master Me@4: (*requestHandler)( currSlot->procrAssignedToSlot->requests ); Me@0: } Me@4: if( currSlot->needsProcrAssigned ) Me@4: { //give slot a new virt procr Me@11: retCode = Me@4: (*slaveScheduler)( currSlot, masterEnv->semanticEnv ); Me@0: Me@11: if( retCode == 1 ) Me@4: { int numFilled = masterEnv->numFilled; Me@4: Me@4: filledSlots[numFilled] = currSlot; Me@4: masterEnv->numFilled += 1; Me@4: Me@4: currSlot->needsProcrAssigned = FALSE; Me@0: } Me@11: else if( retCode == -1 ) //scheduler plug-in says to shut down VMS Me@11: { Me@11: //shutdown -- make "end Thd" virt-procs whose nextInstrPt is the Me@11: // coreloop's EndCoreLoopPt -- causing a jump to the EndThread Me@11: // and any other shut-down. Me@11: } Me@0: } Me@0: } Me@0: Me@4: //put some scheduled slaves in, then continuation, then rest Me@4: numInFirstChunk = masterEnv->numFilled / 2; //tweak this from experiments Me@4: for( filledSlotIdx = 0; filledSlotIdx < numInFirstChunk; filledSlotIdx++) Me@0: { Me@11: writeCASQ( filledSlots[ filledSlotIdx ]->procrAssignedToSlot, workQ ); Me@0: } Me@0: Me@0: //enqueue continuation of this loop Me@0: // note that After this enqueue, continuation might sneak through Me@11: writeCASQ( schedSlots[0]->procrAssignedToSlot, workQ );//master always slot 0 Me@4: for( filledSlotIdx = numInFirstChunk; Me@4: filledSlotIdx < numScheduled; Me@4: filledSlotIdx++) Me@0: { Me@11: writeCASQ( filledSlots[ filledSlotIdx ]->procrAssignedToSlot, workQ ); Me@0: } Me@0: Me@4: masterEnv->numFilled = 0; Me@4: Me@4: //Don't want code above to try to look at requests in masterVirtPr, Me@4: // so leave workDone at FALSE, but do want it to schedule into Me@4: // the slot, so set needs procr assigned to TRUE. Me@4: masterPr->schedSlot->needsProcrAssigned = TRUE; Me@4: Me@4: //Save stack ptr and frame -- don't need to, take out later, but safe Me@4: // Also, wait to set stillRunning to FALSE until just before jump, to Me@4: // protect stack might need to jmp directly to asm busy-wait to be Me@4: // sure stack not touched Me@4: //TODO: gdb check that busy-wait doesn't touch stack, so this is safe Me@4: //don't need any regs to be valid when come back, so clobber list empty Me@4: //TODO: gdb the jmp -- make sure it jumps through register or mem Me@4: asm volatile("movl %%esp, %0; \ Me@4: movl %%ebp, %1; \ Me@4: movl $0x0, %2; \ Me@4: jmp %3 " Me@4: /* outputs */ : "=m" (masterPr->stackPtr), "=m" (masterPr->framePtr), Me@4: "=m" (masterEnv->stillRunning) Me@4: /* inputs */ : "r" (masterPr->coreLoopStartPt) Me@4: /* clobber */ Me@4: ); Me@0: } Me@0: Me@0: