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