annotate VMS.c @ 18:734c665500e4

Kinda have test working -- assembly looks right for saving core loop frame and stack and restoring them But have a bug that looks timing-related, so thinking maybe some threads are going to completion? The whole test isn't quite right yet -- it throws exception because the suspended virtual processors never have a continuation put back into the workQ (is this right? Is that why it throws exception?) Want to move to full code to finish debugging
author Me
date Tue, 22 Jun 2010 12:37:43 -0700
parents 65c8fb2821ee
children 1dbc7f6e3e67
rev   line source
Me@0 1 /*
Me@0 2 * Copyright 2010 OpenSourceCodeStewardshipFoundation
Me@0 3 *
Me@0 4 * Licensed under BSD
Me@0 5 */
Me@0 6
Me@0 7 #include <stdio.h>
Me@0 8 #include <stdlib.h>
Me@0 9 #include <malloc.h>
Me@0 10
Me@0 11 #include "VMS.h"
Me@0 12 #include "Queue_impl/BlockingQueue.h"
Me@0 13
Me@0 14
Me@0 15 /*Setup has two phases:
Me@0 16 * 1) Semantic layer first calls init_VMS, which creates masterEnv, and puts
Me@8 17 * the master virt procr into the work-queue, ready for first "call"
Me@8 18 * 2) Semantic layer then does its own init, which creates the seed virt
Me@8 19 * procr inside the semantic layer, ready to schedule it when
Me@0 20 * asked by the first run of the masterLoop.
Me@0 21 *
Me@0 22 *This part is bit weird because VMS really wants to be "always there", and
Me@0 23 * have applications attach and detach.. for now, this VMS is part of
Me@0 24 * the app, so the VMS system starts up as part of running the app.
Me@0 25 *
Me@8 26 *The semantic layer is isolated from the VMS internals by making the
Me@8 27 * semantic layer do setup to a state that it's ready with its
Me@8 28 * initial virt procrs, ready to schedule them to slots when the masterLoop
Me@0 29 * asks. Without this pattern, the semantic layer's setup would
Me@8 30 * have to modify slots directly to assign the initial virt-procrs, and put
Me@0 31 * them into the workQ itself, breaking the isolation completely.
Me@0 32 *
Me@0 33 *
Me@8 34 *The semantic layer creates the initial virt procr(s), and adds its
Me@8 35 * own environment to masterEnv, and fills in the pointers to
Me@0 36 * the requestHandler and slaveScheduler plug-in functions
Me@8 37 */
Me@8 38
Me@8 39 void
Me@8 40 create_sched_slots( MasterEnv *masterEnv );
Me@8 41
Me@8 42
Me@8 43 /*This allocates VMS data structures, populates the master VMSProc,
Me@0 44 * and master environment, and returns the master environment to the semantic
Me@0 45 * layer.
Me@0 46 */
Me@8 47 void
Me@8 48 VMS__init()
Me@1 49 { MasterEnv *masterEnv;
Me@12 50 CASQueueStruc *workQ;
Me@1 51
Me@0 52 //Make the central work-queue
Me@12 53 _VMSWorkQ = makeCASQ();
Me@1 54 workQ = _VMSWorkQ;
Me@0 55
Me@1 56 _VMSMasterEnv = malloc( sizeof(MasterEnv) );
Me@1 57 masterEnv = _VMSMasterEnv;
Me@0 58
Me@8 59 //create the master virtual processor
Me@8 60 masterEnv->masterVirtPr = VMS__create_procr( &masterLoop, masterEnv );
Me@0 61
Me@1 62 create_sched_slots( masterEnv );
Me@0 63
Me@8 64 //Set slot 0 to be the master virt procr & set flags just in case
Me@8 65 masterEnv->schedSlots[0]->needsProcrAssigned = FALSE; //says don't touch
Me@8 66 masterEnv->schedSlots[0]->workIsDone = FALSE; //says don't touch
Me@1 67 masterEnv->schedSlots[0]->procrAssignedToSlot = masterEnv->masterVirtPr;
Me@1 68
Me@1 69 //First core loop to start up gets this, which will schedule seed Pr
Me@1 70 //TODO: debug: check address of masterVirtPr
Me@12 71 //TODO: commented out for debugging -- put it back in!!
Me@12 72 // writeCASQ( masterEnv->masterVirtPr, workQ );
Me@12 73
Me@12 74 numProcrsCreated = 1;
Me@0 75 }
Me@0 76
Me@0 77
Me@0 78 void
Me@1 79 create_sched_slots( MasterEnv *masterEnv )
Me@8 80 { SchedSlot **schedSlots, **filledSlots;
Me@0 81 int i;
Me@0 82
Me@8 83 schedSlots = malloc( NUM_SCHED_SLOTS * sizeof(SchedSlot *) );
Me@8 84 filledSlots = malloc( NUM_SCHED_SLOTS * sizeof(SchedSlot *) );
Me@8 85 masterEnv->schedSlots = schedSlots;
Me@8 86 masterEnv->filledSlots = filledSlots;
Me@8 87
Me@1 88 for( i = 0; i < NUM_SCHED_SLOTS; i++ )
Me@0 89 {
Me@8 90 schedSlots[i] = malloc( sizeof(SchedSlot) );
Me@8 91
Me@1 92 //Set state to mean "handling requests done, slot needs filling"
Me@8 93 schedSlots[i]->workIsDone = FALSE;
Me@8 94 schedSlots[i]->needsProcrAssigned = TRUE;
Me@0 95 }
Me@0 96 }
Me@0 97
Me@8 98
Me@0 99 /*Semantic layer calls this when it want the system to start running..
Me@0 100 *
Me@0 101 *This creates the core loops, pins them to physical cores, gives them the
Me@0 102 * pointer to the workQ, and starts them running.
Me@0 103 */
Me@12 104 void
Me@0 105 VMS__start()
Me@12 106 { int coreIdx;
Me@0 107
Me@14 108 //TODO: Save "orig" stack pointer and frame ptr -- restore in VMS__end()
Me@8 109 //Create the win threads that animate the core loops
Me@8 110 for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ )
Me@8 111 {
Me@12 112 coreLoopThdParams[coreIdx] = (ThdParams *)malloc( sizeof(ThdParams) );
Me@12 113 coreLoopThdParams[coreIdx]->coreNum = coreIdx;
Me@0 114
Me@12 115 coreLoopThdHandles[coreIdx] =
Me@12 116 CreateThread ( NULL, // Security attributes
Me@12 117 0, // Stack size
Me@12 118 coreLoop,
Me@12 119 coreLoopThdParams[coreIdx],
Me@12 120 CREATE_SUSPENDED,
Me@12 121 &(coreLoopThdIds[coreIdx])
Me@12 122 );
Me@12 123 ResumeThread( coreLoopThdHandles[coreIdx] ); //starts thread
Me@8 124 }
Me@8 125 }
Me@0 126
Me@0 127
Me@0 128
Me@8 129 /*Create stack, then create __cdecl structure on it and put initialData and
Me@8 130 * pointer to the new structure instance into the parameter positions on
Me@8 131 * the stack
Me@8 132 *Then put function pointer into nextInstrPt -- the stack is setup in std
Me@8 133 * call structure, so jumping to function ptr is same as a GCC generated
Me@8 134 * function call
Me@8 135 *No need to save registers on old stack frame, because there's no old
Me@8 136 * animator state to return to --
Me@8 137 *
Me@8 138 */
Me@8 139 VirtProcr *
Me@8 140 VMS__create_procr( VirtProcrFnPtr fnPtr, void *initialData )
Me@8 141 { VirtProcr *newPr;
Me@8 142 char *stackLocs, *stackPtr;
Me@8 143
Me@8 144 newPr = malloc( sizeof(VirtProcr) );
Me@12 145 newPr->procrID = numProcrsCreated++;
Me@8 146 newPr->nextInstrPt = fnPtr;
Me@8 147 newPr->initialData = initialData;
Me@8 148
Me@14 149 //fnPtr takes two params -- void *initData & void *animProcr
Me@8 150 //alloc stack locations, make stackPtr be the highest addr minus room
Me@14 151 // for 2 params + return addr. Return addr (NULL) is in loc pointed to
Me@14 152 // by stackPtr, initData at stackPtr + 4 bytes, animatingPr just above
Me@8 153 stackLocs = malloc( 0x100000 ); //1 meg stack -- default Win thread's size
Me@14 154 stackPtr = ( (char *)stackLocs + 0x100000 - 0x10 );
Me@8 155 //setup __cdecl on stack -- coreloop will switch to stackPtr before jmp
Me@14 156 *( (int *)stackPtr + 2 ) = (int) newPr; //rightmost param -- 32bit pointer
Me@14 157 *( (int *)stackPtr + 1 ) = (int) initialData; //next param to left
Me@8 158 newPr->stackPtr = stackPtr; //core loop will switch to this, then
Me@8 159 newPr->framePtr = stackPtr; //suspend loop will save new stack & frame ptr
Me@8 160
Me@8 161 return newPr;
Me@8 162 }
Me@8 163
Me@8 164
Me@8 165 /*This inserts the semantic-layer's data into the standard VMS carrier
Me@8 166 */
Me@8 167 inline void
Me@8 168 VMS__send_sem_request( void *semReqData, VirtProcr *callingPr )
Me@8 169 { SlaveReqst *req;
Me@8 170
Me@8 171 req = malloc( sizeof(SlaveReqst) );
Me@8 172 req->slaveFrom = callingPr;
Me@8 173 req->semReqData = semReqData;
Me@8 174 req->nextRequest = callingPr->requests;
Me@8 175 callingPr->requests = req;
Me@0 176 }
Me@0 177
Me@0 178 /*there is a label inside this function -- save the addr of this label in
Me@0 179 * the callingPr struc, as the pick-up point from which to start the next
Me@0 180 * work-unit for that procr. If turns out have to save registers, then
Me@0 181 * save them in the procr struc too. Then do assembly jump to the CoreLoop's
Me@0 182 * "done with work-unit" label. The procr struc is in the request in the
Me@0 183 * slave that animated the just-ended work-unit, so all the state is saved
Me@0 184 * there, and will get passed along, inside the request handler, to the
Me@0 185 * next work-unit for that procr.
Me@0 186 */
Me@8 187 void
Me@1 188 VMS__suspend_processor( VirtProcr *callingPr )
Me@14 189 { void *jmpPt, *stackPtrAddr, *framePtrAddr, *coreLoopStackPtr;
Me@14 190 void *coreLoopFramePtr;
Me@14 191 int coreIdx;
Me@0 192
Me@14 193 //The request to master will cause this suspended virt procr to get
Me@14 194 // scheduled again at some future point -- to resume, core loop jumps
Me@14 195 // to the resume point (below), which causes restore of saved regs and
Me@14 196 // "return" from this call.
Me@1 197 callingPr->nextInstrPt = &&ResumePt;
Me@1 198
Me@1 199 //return ownership of the virt procr and sched slot to Master virt pr
Me@1 200 callingPr->schedSlot->workIsDone = TRUE;
Me@14 201 // coreIdx = callingPr->coreAnimatedBy;
Me@1 202
Me@18 203 stackPtrAddr = &(callingPr->stackPtr);
Me@18 204 framePtrAddr = &(callingPr->framePtr);
Me@14 205
Me@14 206 jmpPt = callingPr->coreLoopStartPt;
Me@14 207 coreLoopFramePtr = callingPr->coreLoopFramePtr;//need this only
Me@18 208 coreLoopStackPtr = callingPr->coreLoopStackPtr;//shouldn't need -- safety
Me@1 209
Me@14 210 //Save the virt procr's stack and frame ptrs, restore coreloop's frame
Me@14 211 // ptr, then jump back to "start" of core loop
Me@18 212 asm volatile("movl %0, %%eax; \
Me@18 213 movl %%esp, (%%eax); \
Me@18 214 movl %1, %%eax; \
Me@18 215 movl %%ebp, (%%eax); \
Me@18 216 movl %2, %%eax; \
Me@18 217 movl %3, %%esp; \
Me@18 218 movl %4, %%ebp; \
Me@18 219 jmp %%eax " \
Me@18 220 /* outputs */ : "=g" (stackPtrAddr), "=g" (framePtrAddr) \
Me@18 221 /* inputs */ : "g" (jmpPt), "g"(coreLoopStackPtr), "g"(coreLoopFramePtr)\
Me@18 222 /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx", "%edi","%esi" \
Me@12 223 ); //list everything as clobbered to force GCC to save all
Me@12 224 // live vars that are in regs on stack before this
Me@12 225 // assembly, so that stack pointer is correct, before jmp
Me@1 226
Me@1 227 ResumePt:
Me@0 228 return;
Me@0 229 }
Me@0 230
Me@8 231 void
Me@8 232 VMS__dissipate_animating_processor( VirtProcr *animatingPr )
Me@8 233 {
Me@0 234
Me@1 235 }
Me@1 236
Me@8 237 /*This runs in main thread -- so can only signal to the core loop to shut
Me@8 238 * itself down --
Me@8 239 *
Me@8 240 *Want the master to decide when to shut down -- when semantic layer tells it
Me@8 241 * to -- say, when all the application-virtual processors have dissipated.
Me@8 242 *
Me@8 243 *Maybe return a special code from scheduling plug-in.. master checks and
Me@8 244 * when sees, it shuts down the core loops -- does this by scheduling a
Me@8 245 * special virt processor whose next instr pt is the core-end label.
Me@8 246 */
Me@8 247 void
Me@8 248 VMS__shutdown()
Me@8 249 { int coreIdx;
Me@14 250 VirtProcr *shutDownPr;
Me@8 251
Me@14 252 //TODO: restore the "orig" stack pointer and frame ptr saved in VMS__start
Me@14 253 //create a "special" virtual processor, one for each core loop that has
Me@14 254 // the "loop end" point as its "next instr" point -- when the core loop
Me@14 255 // jumps to animate the virt procr, the jump lands it at its own
Me@14 256 // shut-down code.
Me@8 257 for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ )
Me@8 258 {
Me@14 259 shutDownPr = VMS__create_procr( NULL, NULL );
Me@14 260 shutDownPr->nextInstrPt = _VMSMasterEnv->coreLoopShutDownPt;
Me@8 261 }
Me@12 262 }
Me@12 263
Me@12 264
Me@12 265
Me@12 266 inline TSCount getTSCount()
Me@12 267 { unsigned int low, high;
Me@12 268 TSCount out;
Me@12 269
Me@12 270 saveTimeStampCountInto( low, high );
Me@12 271 out = high;
Me@12 272 out = (out << 32) + low;
Me@12 273 return out;
Me@12 274 }
Me@12 275