Mercurial > cgi-bin > hgwebdir.cgi > VMS > VMS_Implementations > VMS_impls > VMS__MC_shared_impl
comparison MasterLoop.c @ 208:eaf7e4c58c9e
Create common_ancestor brch -- all branches will be closed, then new ones
created with this as the common ancestor of all branches -- it is incomplete!
only code that is common to all HW and Feat and FeatDev branches is in here
| author | Some Random Person <seanhalle@yahoo.com> |
|---|---|
| date | Wed, 22 Feb 2012 11:39:12 -0800 |
| parents | |
| children | 0c83ea8adefc |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 58:9335afc4a64d |
|---|---|
| 1 /* | |
| 2 * Copyright 2010 OpenSourceStewardshipFoundation | |
| 3 * | |
| 4 * Licensed under BSD | |
| 5 */ | |
| 6 | |
| 7 | |
| 8 | |
| 9 #include <stdio.h> | |
| 10 #include <stddef.h> | |
| 11 | |
| 12 #include "VMS.h" | |
| 13 #include "ProcrContext.h" | |
| 14 | |
| 15 | |
| 16 //=========================================================================== | |
| 17 void inline | |
| 18 stealWorkInto( SchedSlot *currSlot, VMSQueueStruc *readyToAnimateQ, | |
| 19 SlaveVP *masterPr ); | |
| 20 | |
| 21 //=========================================================================== | |
| 22 | |
| 23 | |
| 24 | |
| 25 /*This code is animated by the virtual Master processor. | |
| 26 * | |
| 27 *Polls each sched slot exactly once, hands any requests made by a newly | |
| 28 * done slave to the "request handler" plug-in function | |
| 29 * | |
| 30 *Any slots that need a virt procr assigned are given to the "schedule" | |
| 31 * plug-in function, which tries to assign a virt procr (slave) to it. | |
| 32 * | |
| 33 *When all slots needing a processor have been given to the schedule plug-in, | |
| 34 * a fraction of the procrs successfully scheduled are put into the | |
| 35 * work queue, then a continuation of this function is put in, then the rest | |
| 36 * of the virt procrs that were successfully scheduled. | |
| 37 * | |
| 38 *The first thing the continuation does is busy-wait until the previous | |
| 39 * animation completes. This is because an (unlikely) continuation may | |
| 40 * sneak through queue before previous continuation is done putting second | |
| 41 * part of scheduled slaves in, which is the only race condition. | |
| 42 * | |
| 43 */ | |
| 44 | |
| 45 /*May 29, 2010 -- birth a Master during init so that first core loop to | |
| 46 * start running gets it and does all the stuff for a newly born -- | |
| 47 * from then on, will be doing continuation, but do suspension self | |
| 48 * directly at end of master loop | |
| 49 *So VMS__init just births the master virtual processor same way it births | |
| 50 * all the others -- then does any extra setup needed and puts it into the | |
| 51 * work queue. | |
| 52 *However means have to make masterEnv a global static volatile the same way | |
| 53 * did with readyToAnimateQ in core loop. -- for performance, put the | |
| 54 * jump to the core loop directly in here, and have it directly jump back. | |
| 55 * | |
| 56 * | |
| 57 *Aug 18, 2010 -- Going to a separate MasterVP for each core, to see if this | |
| 58 * avoids the suspected bug in the system stack that causes bizarre faults | |
| 59 * at random places in the system code. | |
| 60 * | |
| 61 *So, this function is coupled to each of the MasterVPs, -- meaning this | |
| 62 * function can't rely on a particular stack and frame -- each MasterVP that | |
| 63 * animates this function has a different one. | |
| 64 * | |
| 65 *At this point, the masterLoop does not write itself into the queue anymore, | |
| 66 * instead, the coreLoop acquires the masterLock when it has nothing to | |
| 67 * animate, and then animates its own masterLoop. However, still try to put | |
| 68 * several AppVPs into the queue to amortize the startup cost of switching | |
| 69 * to the MasterVP. Note, don't have to worry about latency of requests much | |
| 70 * because most requests generate work for same core -- only latency issue | |
| 71 * is case when other cores starved and one core's requests generate work | |
| 72 * for them -- so keep max in queue to 3 or 4.. | |
| 73 */ | |
| 74 void masterLoop( void *initData, SlaveVP *animatingPr ) | |
| 75 { | |
| 76 int32 slotIdx, numSlotsFilled; | |
| 77 SlaveVP *schedVirtPr; | |
| 78 SchedSlot *currSlot, **schedSlots; | |
| 79 MasterEnv *masterEnv; | |
| 80 VMSQueueStruc *readyToAnimateQ; | |
| 81 | |
| 82 Sched_Assigner slaveScheduler; | |
| 83 RequestHandler requestHandler; | |
| 84 void *semanticEnv; | |
| 85 | |
| 86 int32 thisCoresIdx; | |
| 87 SlaveVP *masterPr; | |
| 88 volatile SlaveVP *volatileMasterPr; | |
| 89 | |
| 90 volatileMasterPr = animatingPr; | |
| 91 masterPr = (SlaveVP*)volatileMasterPr; //used to force re-define after jmp | |
| 92 | |
| 93 //First animation of each MasterVP will in turn animate this part | |
| 94 // of setup code.. (VP creator sets up the stack as if this function | |
| 95 // was called normally, but actually get here by jmp) | |
| 96 //So, setup values about stack ptr, jmp pt and all that | |
| 97 //masterPr->resumeInstrPtr = &&masterLoopStartPt; | |
| 98 | |
| 99 | |
| 100 //Note, got rid of writing the stack and frame ptr up here, because | |
| 101 // only one | |
| 102 // core can ever animate a given MasterVP, so don't need to communicate | |
| 103 // new frame and stack ptr to the MasterVP storage before a second | |
| 104 // version of that MasterVP can get animated on a different core. | |
| 105 //Also got rid of the busy-wait. | |
| 106 | |
| 107 | |
| 108 //masterLoopStartPt: | |
| 109 while(1){ | |
| 110 | |
| 111 //============================= MEASUREMENT STUFF ======================== | |
| 112 #ifdef MEAS__TIME_MASTER | |
| 113 //Total Master time includes one coreloop time -- just assume the core | |
| 114 // loop time is same for Master as for AppVPs, even though it may be | |
| 115 // smaller due to higher predictability of the fixed jmp. | |
| 116 saveLowTimeStampCountInto( masterPr->startMasterTSCLow ); | |
| 117 #endif | |
| 118 //======================================================================== | |
| 119 | |
| 120 masterEnv = (MasterEnv*)_VMSMasterEnv; | |
| 121 | |
| 122 //GCC may optimize so doesn't always re-define from frame-storage | |
| 123 masterPr = (SlaveVP*)volatileMasterPr; //just to make sure after jmp | |
| 124 thisCoresIdx = masterPr->coreAnimatedBy; | |
| 125 readyToAnimateQ = masterEnv->readyToAnimateQs[thisCoresIdx]; | |
| 126 schedSlots = masterEnv->allSchedSlots[thisCoresIdx]; | |
| 127 | |
| 128 requestHandler = masterEnv->requestHandler; | |
| 129 slaveScheduler = masterEnv->slaveSchedAssigner; | |
| 130 semanticEnv = masterEnv->semanticEnv; | |
| 131 | |
| 132 | |
| 133 //Poll each slot's Done flag | |
| 134 numSlotsFilled = 0; | |
| 135 for( slotIdx = 0; slotIdx < NUM_SCHED_SLOTS; slotIdx++) | |
| 136 { | |
| 137 currSlot = schedSlots[ slotIdx ]; | |
| 138 | |
| 139 if( currSlot->workIsDone ) | |
| 140 { | |
| 141 currSlot->workIsDone = FALSE; | |
| 142 currSlot->needsProcrAssigned = TRUE; | |
| 143 | |
| 144 //process requests from slave to master | |
| 145 //====================== MEASUREMENT STUFF =================== | |
| 146 #ifdef MEAS__TIME_PLUGIN | |
| 147 int32 startStamp1, endStamp1; | |
| 148 saveLowTimeStampCountInto( startStamp1 ); | |
| 149 #endif | |
| 150 //============================================================ | |
| 151 (*requestHandler)( currSlot->procrAssignedToSlot, semanticEnv ); | |
| 152 //====================== MEASUREMENT STUFF =================== | |
| 153 #ifdef MEAS__TIME_PLUGIN | |
| 154 saveLowTimeStampCountInto( endStamp1 ); | |
| 155 addIntervalToHist( startStamp1, endStamp1, | |
| 156 _VMSMasterEnv->reqHdlrLowTimeHist ); | |
| 157 addIntervalToHist( startStamp1, endStamp1, | |
| 158 _VMSMasterEnv->reqHdlrHighTimeHist ); | |
| 159 #endif | |
| 160 //============================================================ | |
| 161 } | |
| 162 if( currSlot->needsProcrAssigned ) | |
| 163 { //give slot a new virt procr | |
| 164 schedVirtPr = | |
| 165 (*slaveScheduler)( semanticEnv, thisCoresIdx ); | |
| 166 | |
| 167 if( schedVirtPr != NULL ) | |
| 168 { currSlot->procrAssignedToSlot = schedVirtPr; | |
| 169 schedVirtPr->schedSlot = currSlot; | |
| 170 currSlot->needsProcrAssigned = FALSE; | |
| 171 numSlotsFilled += 1; | |
| 172 | |
| 173 writeVMSQ( schedVirtPr, readyToAnimateQ ); | |
| 174 } | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 | |
| 179 #ifdef USE_WORK_STEALING | |
| 180 //If no slots filled, means no more work, look for work to steal. | |
| 181 if( numSlotsFilled == 0 ) | |
| 182 { gateProtected_stealWorkInto( currSlot, readyToAnimateQ, masterPr ); | |
| 183 } | |
| 184 #endif | |
| 185 | |
| 186 | |
| 187 #ifdef MEAS__TIME_MASTER | |
| 188 saveLowTimeStampCountInto( masterPr->endMasterTSCLow ); | |
| 189 #endif | |
| 190 | |
| 191 masterSwitchToCoreLoop(animatingPr); | |
| 192 flushRegisters(); | |
| 193 }//MasterLoop | |
| 194 | |
| 195 | |
| 196 } | |
| 197 | |
| 198 | |
| 199 | |
| 200 /*This has a race condition -- the coreloops are accessing their own queues | |
| 201 * at the same time that this work-stealer on a different core is trying to | |
| 202 */ | |
| 203 void inline | |
| 204 stealWorkInto( SchedSlot *currSlot, VMSQueueStruc *readyToAnimateQ, | |
| 205 SlaveVP *masterPr ) | |
| 206 { | |
| 207 SlaveVP *stolenPr; | |
| 208 int32 coreIdx, i; | |
| 209 VMSQueueStruc *currQ; | |
| 210 | |
| 211 stolenPr = NULL; | |
| 212 coreIdx = masterPr->coreAnimatedBy; | |
| 213 for( i = 0; i < NUM_CORES -1; i++ ) | |
| 214 { | |
| 215 if( coreIdx >= NUM_CORES -1 ) | |
| 216 { coreIdx = 0; | |
| 217 } | |
| 218 else | |
| 219 { coreIdx++; | |
| 220 } | |
| 221 currQ = _VMSMasterEnv->readyToAnimateQs[coreIdx]; | |
| 222 if( numInVMSQ( currQ ) > 0 ) | |
| 223 { stolenPr = readVMSQ (currQ ); | |
| 224 break; | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 if( stolenPr != NULL ) | |
| 229 { currSlot->procrAssignedToSlot = stolenPr; | |
| 230 stolenPr->schedSlot = currSlot; | |
| 231 currSlot->needsProcrAssigned = FALSE; | |
| 232 | |
| 233 writeVMSQ( stolenPr, readyToAnimateQ ); | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 /*This algorithm makes the common case fast. Make the coreloop passive, | |
| 238 * and show its progress. Make the stealer control a gate that coreloop | |
| 239 * has to pass. | |
| 240 *To avoid interference, only one stealer at a time. Use a global | |
| 241 * stealer-lock. | |
| 242 * | |
| 243 *The pattern is based on a gate -- stealer shuts the gate, then monitors | |
| 244 * to be sure any already past make it all the way out, before starting. | |
| 245 *So, have a "progress" measure just before the gate, then have two after it, | |
| 246 * one is in a "waiting room" outside the gate, the other is at the exit. | |
| 247 *Then, the stealer first shuts the gate, then checks the progress measure | |
| 248 * outside it, then looks to see if the progress measure at the exit is the | |
| 249 * same. If yes, it knows the protected area is empty 'cause no other way | |
| 250 * to get in and the last to get in also exited. | |
| 251 *If the progress measure at the exit is not the same, then the stealer goes | |
| 252 * into a loop checking both the waiting-area and the exit progress-measures | |
| 253 * until one of them shows the same as the measure outside the gate. Might | |
| 254 * as well re-read the measure outside the gate each go around, just to be | |
| 255 * sure. It is guaranteed that one of the two will eventually match the one | |
| 256 * outside the gate. | |
| 257 * | |
| 258 *Here's an informal proof of correctness: | |
| 259 *The gate can be closed at any point, and have only four cases: | |
| 260 * 1) coreloop made it past the gate-closing but not yet past the exit | |
| 261 * 2) coreloop made it past the pre-gate progress update but not yet past | |
| 262 * the gate, | |
| 263 * 3) coreloop is right before the pre-gate update | |
| 264 * 4) coreloop is past the exit and far from the pre-gate update. | |
| 265 * | |
| 266 * Covering the cases in reverse order, | |
| 267 * 4) is not a problem -- stealer will read pre-gate progress, see that it | |
| 268 * matches exit progress, and the gate is closed, so stealer can proceed. | |
| 269 * 3) stealer will read pre-gate progress just after coreloop updates it.. | |
| 270 * so stealer goes into a loop until the coreloop causes wait-progress | |
| 271 * to match pre-gate progress, so then stealer can proceed | |
| 272 * 2) same as 3.. | |
| 273 * 1) stealer reads pre-gate progress, sees that it's different than exit, | |
| 274 * so goes into loop until exit matches pre-gate, now it knows coreloop | |
| 275 * is not in protected and cannot get back in, so can proceed. | |
| 276 * | |
| 277 *Implementation for the stealer: | |
| 278 * | |
| 279 *First, acquire the stealer lock -- only cores with no work to do will | |
| 280 * compete to steal, so not a big performance penalty having only one -- | |
| 281 * will rarely have multiple stealers in a system with plenty of work -- and | |
| 282 * in a system with little work, it doesn't matter. | |
| 283 * | |
| 284 *Note, have single-reader, single-writer pattern for all variables used to | |
| 285 * communicate between stealer and victims | |
| 286 * | |
| 287 *So, scan the queues of the core loops, until find non-empty. Each core | |
| 288 * has its own list that it scans. The list goes in order from closest to | |
| 289 * furthest core, so it steals first from close cores. Later can add | |
| 290 * taking info from the app about overlapping footprints, and scan all the | |
| 291 * others then choose work with the most footprint overlap with the contents | |
| 292 * of this core's cache. | |
| 293 * | |
| 294 *Now, have a victim want to take work from. So, shut the gate in that | |
| 295 * coreloop, by setting the "gate closed" var on its stack to TRUE. | |
| 296 *Then, read the core's pre-gate progress and compare to the core's exit | |
| 297 * progress. | |
| 298 *If same, can proceed to take work from the coreloop's queue. When done, | |
| 299 * write FALSE to gate closed var. | |
| 300 *If different, then enter a loop that reads the pre-gate progress, then | |
| 301 * compares to exit progress then to wait progress. When one of two | |
| 302 * matches, proceed. Take work from the coreloop's queue. When done, | |
| 303 * write FALSE to the gate closed var. | |
| 304 * | |
| 305 */ | |
| 306 void inline | |
| 307 gateProtected_stealWorkInto( SchedSlot *currSlot, | |
| 308 VMSQueueStruc *myReadyToAnimateQ, | |
| 309 SlaveVP *masterPr ) | |
| 310 { | |
| 311 SlaveVP *stolenPr; | |
| 312 int32 coreIdx, i, haveAVictim, gotLock; | |
| 313 VMSQueueStruc *victimsQ; | |
| 314 | |
| 315 volatile GateStruc *vicGate; | |
| 316 int32 coreMightBeInProtected; | |
| 317 | |
| 318 | |
| 319 | |
| 320 //see if any other cores have work available to steal | |
| 321 haveAVictim = FALSE; | |
| 322 coreIdx = masterPr->coreAnimatedBy; | |
| 323 for( i = 0; i < NUM_CORES -1; i++ ) | |
| 324 { | |
| 325 if( coreIdx >= NUM_CORES -1 ) | |
| 326 { coreIdx = 0; | |
| 327 } | |
| 328 else | |
| 329 { coreIdx++; | |
| 330 } | |
| 331 victimsQ = _VMSMasterEnv->readyToAnimateQs[coreIdx]; | |
| 332 if( numInVMSQ( victimsQ ) > 0 ) | |
| 333 { haveAVictim = TRUE; | |
| 334 vicGate = _VMSMasterEnv->workStealingGates[ coreIdx ]; | |
| 335 break; | |
| 336 } | |
| 337 } | |
| 338 if( !haveAVictim ) return; //no work to steal, exit | |
| 339 | |
| 340 //have a victim core, now get the stealer-lock | |
| 341 gotLock =__sync_bool_compare_and_swap( &(_VMSMasterEnv->workStealingLock), | |
| 342 UNLOCKED, LOCKED ); | |
| 343 if( !gotLock ) return; //go back to core loop, which will re-start master | |
| 344 | |
| 345 | |
| 346 //====== Start Gate-protection ======= | |
| 347 vicGate->gateClosed = TRUE; | |
| 348 coreMightBeInProtected= vicGate->preGateProgress != vicGate->exitProgress; | |
| 349 while( coreMightBeInProtected ) | |
| 350 { //wait until sure | |
| 351 if( vicGate->preGateProgress == vicGate->waitProgress ) | |
| 352 coreMightBeInProtected = FALSE; | |
| 353 if( vicGate->preGateProgress == vicGate->exitProgress ) | |
| 354 coreMightBeInProtected = FALSE; | |
| 355 } | |
| 356 | |
| 357 stolenPr = readVMSQ ( victimsQ ); | |
| 358 | |
| 359 vicGate->gateClosed = FALSE; | |
| 360 //======= End Gate-protection ======= | |
| 361 | |
| 362 | |
| 363 if( stolenPr != NULL ) //victim could have been in protected and taken | |
| 364 { currSlot->procrAssignedToSlot = stolenPr; | |
| 365 stolenPr->schedSlot = currSlot; | |
| 366 currSlot->needsProcrAssigned = FALSE; | |
| 367 | |
| 368 writeVMSQ( stolenPr, myReadyToAnimateQ ); | |
| 369 } | |
| 370 | |
| 371 //unlock the work stealing lock | |
| 372 _VMSMasterEnv->workStealingLock = UNLOCKED; | |
| 373 } |
