Mercurial > cgi-bin > hgwebdir.cgi > VMS > VMS_Implementations > DKU_impls > DKU__MC_shared_impl
comparison DKU.c @ 0:9f2a7bd26dd9
Initial add -- code is straight copy of VSs implementation.. to be modified
| author | Sean Halle <seanhalle@yahoo.com> |
|---|---|
| date | Mon, 27 Aug 2012 02:14:35 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:fc15afaf3a59 |
|---|---|
| 1 /* | |
| 2 * Copyright 2010 OpenSourceCodeStewardshipFoundation | |
| 3 * | |
| 4 * Licensed under BSD | |
| 5 */ | |
| 6 | |
| 7 #include <stdio.h> | |
| 8 #include <stdlib.h> | |
| 9 #include <malloc.h> | |
| 10 | |
| 11 #include "Queue_impl/PrivateQueue.h" | |
| 12 #include "Hash_impl/PrivateHash.h" | |
| 13 | |
| 14 #include "VSs.h" | |
| 15 #include "Measurement/VSs_Counter_Recording.h" | |
| 16 | |
| 17 //========================================================================== | |
| 18 | |
| 19 void | |
| 20 VSs__init(); | |
| 21 | |
| 22 void | |
| 23 VSs__init_Helper(); | |
| 24 //========================================================================== | |
| 25 | |
| 26 | |
| 27 | |
| 28 //=========================================================================== | |
| 29 | |
| 30 | |
| 31 /*These are the library functions *called in the application* | |
| 32 * | |
| 33 *There's a pattern for the outside sequential code to interact with the | |
| 34 * VMS_HW code. | |
| 35 *The VMS_HW system is inside a boundary.. every VSs system is in its | |
| 36 * own directory that contains the functions for each of the processor types. | |
| 37 * One of the processor types is the "seed" processor that starts the | |
| 38 * cascade of creating all the processors that do the work. | |
| 39 *So, in the directory is a file called "EntryPoint.c" that contains the | |
| 40 * function, named appropriately to the work performed, that the outside | |
| 41 * sequential code calls. This function follows a pattern: | |
| 42 *1) it calls VSs__init() | |
| 43 *2) it creates the initial data for the seed processor, which is passed | |
| 44 * in to the function | |
| 45 *3) it creates the seed VSs processor, with the data to start it with. | |
| 46 *4) it calls startVSsThenWaitUntilWorkDone | |
| 47 *5) it gets the returnValue from the transfer struc and returns that | |
| 48 * from the function | |
| 49 * | |
| 50 *For now, a new VSs system has to be created via VSs__init every | |
| 51 * time an entry point function is called -- later, might add letting the | |
| 52 * VSs system be created once, and let all the entry points just reuse | |
| 53 * it -- want to be as simple as possible now, and see by using what makes | |
| 54 * sense for later.. | |
| 55 */ | |
| 56 | |
| 57 | |
| 58 | |
| 59 //=========================================================================== | |
| 60 | |
| 61 /*This is the "border crossing" function -- the thing that crosses from the | |
| 62 * outside world, into the VMS_HW world. It initializes and starts up the | |
| 63 * VMS system, then creates one processor from the specified function and | |
| 64 * puts it into the readyQ. From that point, that one function is resp. | |
| 65 * for creating all the other processors, that then create others, and so | |
| 66 * forth. | |
| 67 *When all the processors, including the seed, have dissipated, then this | |
| 68 * function returns. The results will have been written by side-effect via | |
| 69 * pointers read from, or written into initData. | |
| 70 * | |
| 71 *NOTE: no Threads should exist in the outside program that might touch | |
| 72 * any of the data reachable from initData passed in to here | |
| 73 */ | |
| 74 void | |
| 75 VSs__create_seed_slave_and_do_work( TopLevelFnPtr fnPtr, void *initData ) | |
| 76 { VSsSemEnv *semEnv; | |
| 77 SlaveVP *seedSlv; | |
| 78 VSsSemData *semData; | |
| 79 VSsTaskStub *threadTaskStub, *parentTaskStub; | |
| 80 | |
| 81 VSs__init(); //normal multi-thd | |
| 82 | |
| 83 semEnv = _VMSMasterEnv->semanticEnv; | |
| 84 | |
| 85 //VSs starts with one processor, which is put into initial environ, | |
| 86 // and which then calls create() to create more, thereby expanding work | |
| 87 seedSlv = VSs__create_slave_helper( fnPtr, initData, | |
| 88 semEnv, semEnv->nextCoreToGetNewSlv++ ); | |
| 89 | |
| 90 //seed slave is a thread slave, so make a thread's task stub for it | |
| 91 // and then make another to stand for the seed's parent task. Make | |
| 92 // the parent be already ended, and have one child (the seed). This | |
| 93 // will make the dissipate handler do the right thing when the seed | |
| 94 // is dissipated. | |
| 95 threadTaskStub = create_thread_task_stub( initData ); | |
| 96 parentTaskStub = create_thread_task_stub( NULL ); | |
| 97 parentTaskStub->isEnded = TRUE; | |
| 98 parentTaskStub->numLiveChildThreads = 1; //so dissipate works for seed | |
| 99 threadTaskStub->parentTaskStub = parentTaskStub; | |
| 100 | |
| 101 semData = (VSsSemData *)seedSlv->semanticData; | |
| 102 //seedVP is a thread, so has a permanent task | |
| 103 semData->needsTaskAssigned = FALSE; | |
| 104 semData->taskStub = threadTaskStub; | |
| 105 semData->slaveType = ThreadSlv; | |
| 106 | |
| 107 resume_slaveVP( seedSlv, semEnv ); //returns right away, just queues Slv | |
| 108 | |
| 109 VMS_SS__start_the_work_then_wait_until_done(); //normal multi-thd | |
| 110 | |
| 111 VSs__cleanup_after_shutdown(); | |
| 112 } | |
| 113 | |
| 114 | |
| 115 int32 | |
| 116 VSs__giveMinWorkUnitCycles( float32 percentOverhead ) | |
| 117 { | |
| 118 return MIN_WORK_UNIT_CYCLES; | |
| 119 } | |
| 120 | |
| 121 int32 | |
| 122 VSs__giveIdealNumWorkUnits() | |
| 123 { | |
| 124 return NUM_ANIM_SLOTS * NUM_CORES; | |
| 125 } | |
| 126 | |
| 127 int32 | |
| 128 VSs__give_number_of_cores_to_schedule_onto() | |
| 129 { | |
| 130 return NUM_CORES; | |
| 131 } | |
| 132 | |
| 133 /*For now, use TSC -- later, make these two macros with assembly that first | |
| 134 * saves jump point, and second jumps back several times to get reliable time | |
| 135 */ | |
| 136 void | |
| 137 VSs__start_primitive() | |
| 138 { saveLowTimeStampCountInto( ((VSsSemEnv *)(_VMSMasterEnv->semanticEnv))-> | |
| 139 primitiveStartTime ); | |
| 140 } | |
| 141 | |
| 142 /*Just quick and dirty for now -- make reliable later | |
| 143 * will want this to jump back several times -- to be sure cache is warm | |
| 144 * because don't want comm time included in calc-time measurement -- and | |
| 145 * also to throw out any "weird" values due to OS interrupt or TSC rollover | |
| 146 */ | |
| 147 int32 | |
| 148 VSs__end_primitive_and_give_cycles() | |
| 149 { int32 endTime, startTime; | |
| 150 //TODO: fix by repeating time-measurement | |
| 151 saveLowTimeStampCountInto( endTime ); | |
| 152 startTime =((VSsSemEnv*)(_VMSMasterEnv->semanticEnv))->primitiveStartTime; | |
| 153 return (endTime - startTime); | |
| 154 } | |
| 155 | |
| 156 //=========================================================================== | |
| 157 | |
| 158 /*Initializes all the data-structures for a VSs system -- but doesn't | |
| 159 * start it running yet! | |
| 160 * | |
| 161 *This runs in the main thread -- before VMS starts up | |
| 162 * | |
| 163 *This sets up the semantic layer over the VMS system | |
| 164 * | |
| 165 *First, calls VMS_Setup, then creates own environment, making it ready | |
| 166 * for creating the seed processor and then starting the work. | |
| 167 */ | |
| 168 void | |
| 169 VSs__init() | |
| 170 { | |
| 171 VMS_SS__init(); | |
| 172 //masterEnv, a global var, now is partially set up by init_VMS | |
| 173 // after this, have VMS_int__malloc and VMS_int__free available | |
| 174 | |
| 175 VSs__init_Helper(); | |
| 176 } | |
| 177 | |
| 178 | |
| 179 void idle_fn(void* data, SlaveVP *animatingSlv){ | |
| 180 while(1){ | |
| 181 VMS_int__suspend_slaveVP_and_send_req(animatingSlv); | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 void | |
| 186 VSs__init_Helper() | |
| 187 { VSsSemEnv *semanticEnv; | |
| 188 int32 i, coreNum, slotNum; | |
| 189 VSsSemData *semData; | |
| 190 | |
| 191 //Hook up the semantic layer's plug-ins to the Master virt procr | |
| 192 _VMSMasterEnv->requestHandler = &VSs__Request_Handler; | |
| 193 _VMSMasterEnv->slaveAssigner = &VSs__assign_slaveVP_to_slot; | |
| 194 | |
| 195 //create the semantic layer's environment (all its data) and add to | |
| 196 // the master environment | |
| 197 semanticEnv = VMS_int__malloc( sizeof( VSsSemEnv ) ); | |
| 198 _VMSMasterEnv->semanticEnv = semanticEnv; | |
| 199 | |
| 200 #ifdef HOLISTIC__TURN_ON_PERF_COUNTERS | |
| 201 _VMSMasterEnv->counterHandler = &VSs__counter_handler; | |
| 202 VSs__init_counter_data_structs(); | |
| 203 #endif | |
| 204 | |
| 205 semanticEnv->shutdownInitiated = FALSE; | |
| 206 semanticEnv->coreIsDone = VMS_int__malloc( NUM_CORES * sizeof( bool32 ) ); | |
| 207 //For each animation slot, there is an idle slave, and an initial | |
| 208 // slave assigned as the current-task-slave. Create them here. | |
| 209 SlaveVP *idleSlv, *slotTaskSlv; | |
| 210 for( coreNum = 0; coreNum < NUM_CORES; coreNum++ ) | |
| 211 { semanticEnv->coreIsDone[coreNum] = FALSE; //use during shutdown | |
| 212 | |
| 213 for( slotNum = 0; slotNum < NUM_ANIM_SLOTS; ++slotNum ) | |
| 214 { idleSlv = VSs__create_slave_helper( &idle_fn, NULL, semanticEnv, 0); | |
| 215 idleSlv->coreAnimatedBy = coreNum; | |
| 216 idleSlv->animSlotAssignedTo = | |
| 217 _VMSMasterEnv->allAnimSlots[coreNum][slotNum]; | |
| 218 semanticEnv->idleSlv[coreNum][slotNum] = idleSlv; | |
| 219 | |
| 220 slotTaskSlv = VSs__create_slave_helper( &idle_fn, NULL, semanticEnv, 0); | |
| 221 slotTaskSlv->coreAnimatedBy = coreNum; | |
| 222 slotTaskSlv->animSlotAssignedTo = | |
| 223 _VMSMasterEnv->allAnimSlots[coreNum][slotNum]; | |
| 224 | |
| 225 semData = slotTaskSlv->semanticData; | |
| 226 semData->needsTaskAssigned = TRUE; | |
| 227 semData->slaveType = SlotTaskSlv; | |
| 228 semanticEnv->slotTaskSlvs[coreNum][slotNum] = slotTaskSlv; | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 //create the ready queues, hash tables used for matching and so forth | |
| 233 semanticEnv->slavesReadyToResumeQ = makeVMSQ(); | |
| 234 semanticEnv->freeExtraTaskSlvQ = makeVMSQ(); | |
| 235 semanticEnv->taskReadyQ = makeVMSQ(); | |
| 236 | |
| 237 semanticEnv->argPtrHashTbl = makeHashTable32( 16, &VMS_int__free ); | |
| 238 semanticEnv->commHashTbl = makeHashTable32( 16, &VMS_int__free ); | |
| 239 | |
| 240 semanticEnv->nextCoreToGetNewSlv = 0; | |
| 241 | |
| 242 | |
| 243 //TODO: bug -- turn these arrays into dyn arrays to eliminate limit | |
| 244 //semanticEnv->singletonHasBeenExecutedFlags = makeDynArrayInfo( ); | |
| 245 //semanticEnv->transactionStrucs = makeDynArrayInfo( ); | |
| 246 for( i = 0; i < NUM_STRUCS_IN_SEM_ENV; i++ ) | |
| 247 { | |
| 248 semanticEnv->fnSingletons[i].endInstrAddr = NULL; | |
| 249 semanticEnv->fnSingletons[i].hasBeenStarted = FALSE; | |
| 250 semanticEnv->fnSingletons[i].hasFinished = FALSE; | |
| 251 semanticEnv->fnSingletons[i].waitQ = makeVMSQ(); | |
| 252 semanticEnv->transactionStrucs[i].waitingVPQ = makeVMSQ(); | |
| 253 } | |
| 254 | |
| 255 semanticEnv->numLiveExtraTaskSlvs = 0; //must be last | |
| 256 semanticEnv->numLiveThreadSlvs = 1; //must be last, counts the seed | |
| 257 | |
| 258 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC | |
| 259 semanticEnv->unitList = makeListOfArrays(sizeof(Unit),128); | |
| 260 semanticEnv->ctlDependenciesList = makeListOfArrays(sizeof(Dependency),128); | |
| 261 semanticEnv->commDependenciesList = makeListOfArrays(sizeof(Dependency),128); | |
| 262 semanticEnv->dynDependenciesList = makeListOfArrays(sizeof(Dependency),128); | |
| 263 semanticEnv->ntonGroupsInfo = makePrivDynArrayOfSize((void***)&(semanticEnv->ntonGroups),8); | |
| 264 | |
| 265 semanticEnv->hwArcs = makeListOfArrays(sizeof(Dependency),128); | |
| 266 memset(semanticEnv->last_in_slot,0,sizeof(NUM_CORES * NUM_ANIM_SLOTS * sizeof(Unit))); | |
| 267 #endif | |
| 268 } | |
| 269 | |
| 270 | |
| 271 /*Frees any memory allocated by VSs__init() then calls VMS_int__shutdown | |
| 272 */ | |
| 273 void | |
| 274 VSs__cleanup_after_shutdown() | |
| 275 { VSsSemEnv *semanticEnv; | |
| 276 | |
| 277 semanticEnv = _VMSMasterEnv->semanticEnv; | |
| 278 | |
| 279 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC | |
| 280 //UCC | |
| 281 FILE* output; | |
| 282 int n; | |
| 283 char filename[255]; | |
| 284 for(n=0;n<255;n++) | |
| 285 { | |
| 286 sprintf(filename, "./counters/UCC.%d",n); | |
| 287 output = fopen(filename,"r"); | |
| 288 if(output) | |
| 289 { | |
| 290 fclose(output); | |
| 291 }else{ | |
| 292 break; | |
| 293 } | |
| 294 } | |
| 295 if(n<255){ | |
| 296 printf("Saving UCC to File: %s ...\n", filename); | |
| 297 output = fopen(filename,"w+"); | |
| 298 if(output!=NULL){ | |
| 299 set_dependency_file(output); | |
| 300 //fprintf(output,"digraph Dependencies {\n"); | |
| 301 //set_dot_file(output); | |
| 302 //FIXME: first line still depends on counters being enabled, replace w/ unit struct! | |
| 303 //forAllInDynArrayDo(_VMSMasterEnv->counter_history_array_info, &print_dot_node_info ); | |
| 304 forAllInListOfArraysDo(semanticEnv->unitList, &print_unit_to_file); | |
| 305 forAllInListOfArraysDo( semanticEnv->commDependenciesList, &print_comm_dependency_to_file ); | |
| 306 forAllInListOfArraysDo( semanticEnv->ctlDependenciesList, &print_ctl_dependency_to_file ); | |
| 307 forAllInDynArrayDo(semanticEnv->ntonGroupsInfo,&print_nton_to_file); | |
| 308 //fprintf(output,"}\n"); | |
| 309 fflush(output); | |
| 310 | |
| 311 } else | |
| 312 printf("Opening UCC file failed. Please check that folder \"counters\" exists in run directory and has write permission.\n"); | |
| 313 } else { | |
| 314 printf("Could not open UCC file, please clean \"counters\" folder. (Must contain less than 255 files.)\n"); | |
| 315 } | |
| 316 //Loop Graph | |
| 317 for(n=0;n<255;n++) | |
| 318 { | |
| 319 sprintf(filename, "./counters/LoopGraph.%d",n); | |
| 320 output = fopen(filename,"r"); | |
| 321 if(output) | |
| 322 { | |
| 323 fclose(output); | |
| 324 }else{ | |
| 325 break; | |
| 326 } | |
| 327 } | |
| 328 if(n<255){ | |
| 329 printf("Saving LoopGraph to File: %s ...\n", filename); | |
| 330 output = fopen(filename,"w+"); | |
| 331 if(output!=NULL){ | |
| 332 set_dependency_file(output); | |
| 333 //fprintf(output,"digraph Dependencies {\n"); | |
| 334 //set_dot_file(output); | |
| 335 //FIXME: first line still depends on counters being enabled, replace w/ unit struct! | |
| 336 //forAllInDynArrayDo(_VMSMasterEnv->counter_history_array_info, &print_dot_node_info ); | |
| 337 forAllInListOfArraysDo( semanticEnv->unitList, &print_unit_to_file ); | |
| 338 forAllInListOfArraysDo( semanticEnv->commDependenciesList, &print_comm_dependency_to_file ); | |
| 339 forAllInListOfArraysDo( semanticEnv->ctlDependenciesList, &print_ctl_dependency_to_file ); | |
| 340 forAllInListOfArraysDo( semanticEnv->dynDependenciesList, &print_dyn_dependency_to_file ); | |
| 341 forAllInListOfArraysDo( semanticEnv->hwArcs, &print_hw_dependency_to_file ); | |
| 342 //fprintf(output,"}\n"); | |
| 343 fflush(output); | |
| 344 | |
| 345 } else | |
| 346 printf("Opening LoopGraph file failed. Please check that folder \"counters\" exists in run directory and has write permission.\n"); | |
| 347 } else { | |
| 348 printf("Could not open LoopGraph file, please clean \"counters\" folder. (Must contain less than 255 files.)\n"); | |
| 349 } | |
| 350 | |
| 351 | |
| 352 freeListOfArrays(semanticEnv->unitList); | |
| 353 freeListOfArrays(semanticEnv->commDependenciesList); | |
| 354 freeListOfArrays(semanticEnv->ctlDependenciesList); | |
| 355 freeListOfArrays(semanticEnv->dynDependenciesList); | |
| 356 | |
| 357 #endif | |
| 358 #ifdef HOLISTIC__TURN_ON_PERF_COUNTERS | |
| 359 for(n=0;n<255;n++) | |
| 360 { | |
| 361 sprintf(filename, "./counters/Counters.%d.csv",n); | |
| 362 output = fopen(filename,"r"); | |
| 363 if(output) | |
| 364 { | |
| 365 fclose(output); | |
| 366 }else{ | |
| 367 break; | |
| 368 } | |
| 369 } | |
| 370 if(n<255){ | |
| 371 printf("Saving Counter measurements to File: %s ...\n", filename); | |
| 372 output = fopen(filename,"w+"); | |
| 373 if(output!=NULL){ | |
| 374 set_counter_file(output); | |
| 375 int i; | |
| 376 for(i=0;i<NUM_CORES;i++){ | |
| 377 forAllInListOfArraysDo( semanticEnv->counterList[i], &print_counter_events_to_file ); | |
| 378 fflush(output); | |
| 379 } | |
| 380 | |
| 381 } else | |
| 382 printf("Opening UCC file failed. Please check that folder \"counters\" exists in run directory and has write permission.\n"); | |
| 383 } else { | |
| 384 printf("Could not open UCC file, please clean \"counters\" folder. (Must contain less than 255 files.)\n"); | |
| 385 } | |
| 386 | |
| 387 #endif | |
| 388 /* It's all allocated inside VMS's big chunk -- that's about to be freed, so | |
| 389 * nothing to do here | |
| 390 | |
| 391 | |
| 392 for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) | |
| 393 { | |
| 394 VMS_int__free( semanticEnv->readyVPQs[coreIdx]->startOfData ); | |
| 395 VMS_int__free( semanticEnv->readyVPQs[coreIdx] ); | |
| 396 } | |
| 397 VMS_int__free( semanticEnv->readyVPQs ); | |
| 398 | |
| 399 freeHashTable( semanticEnv->commHashTbl ); | |
| 400 VMS_int__free( _VMSMasterEnv->semanticEnv ); | |
| 401 */ | |
| 402 VMS_SS__cleanup_at_end_of_shutdown(); | |
| 403 } | |
| 404 | |
| 405 | |
| 406 //=========================================================================== | |
| 407 | |
| 408 SlaveVP * | |
| 409 VSs__create_thread( TopLevelFnPtr fnPtr, void *initData, | |
| 410 SlaveVP *creatingThd ) | |
| 411 { VSsSemReq reqData; | |
| 412 | |
| 413 //the semantic request data is on the stack and disappears when this | |
| 414 // call returns -- it's guaranteed to remain in the VP's stack for as | |
| 415 // long as the VP is suspended. | |
| 416 reqData.reqType = 0; //know type because in a VMS create req | |
| 417 reqData.fnPtr = fnPtr; | |
| 418 reqData.initData = initData; | |
| 419 reqData.callingSlv = creatingThd; | |
| 420 | |
| 421 VMS_WL__send_create_slaveVP_req( &reqData, creatingThd ); | |
| 422 | |
| 423 return creatingThd->dataRetFromReq; | |
| 424 } | |
| 425 | |
| 426 /*This is always the last thing done in the code animated by a thread VP. | |
| 427 * Normally, this would be the last line of the thread's top level function. | |
| 428 * But, if the thread exits from any point, it has to do so by calling | |
| 429 * this. | |
| 430 * | |
| 431 *It simply sends a dissipate request, which handles all the state cleanup. | |
| 432 */ | |
| 433 void | |
| 434 VSs__end_thread( SlaveVP *thdToEnd ) | |
| 435 { VSsSemData *semData; | |
| 436 | |
| 437 VMS_WL__send_dissipate_req( thdToEnd ); | |
| 438 } | |
| 439 | |
| 440 | |
| 441 | |
| 442 //=========================================================================== | |
| 443 | |
| 444 | |
| 445 //======================= task submit and end ============================== | |
| 446 /* | |
| 447 */ | |
| 448 void | |
| 449 VSs__submit_task( VSsTaskType *taskType, void *args, SlaveVP *animSlv) | |
| 450 { VSsSemReq reqData; | |
| 451 | |
| 452 reqData.reqType = submit_task; | |
| 453 | |
| 454 reqData.taskType = taskType; | |
| 455 reqData.args = args; | |
| 456 reqData.callingSlv = animSlv; | |
| 457 | |
| 458 reqData.taskID = NULL; | |
| 459 | |
| 460 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 461 } | |
| 462 | |
| 463 inline int32 * | |
| 464 VSs__create_taskID_of_size( int32 numInts, SlaveVP *animSlv ) | |
| 465 { int32 *taskID; | |
| 466 | |
| 467 taskID = VMS_WL__malloc( sizeof(int32) + numInts * sizeof(int32) ); | |
| 468 taskID[0] = numInts; | |
| 469 return taskID; | |
| 470 } | |
| 471 | |
| 472 void | |
| 473 VSs__submit_task_with_ID( VSsTaskType *taskType, void *args, int32 *taskID, | |
| 474 SlaveVP *animSlv) | |
| 475 { VSsSemReq reqData; | |
| 476 | |
| 477 reqData.reqType = submit_task; | |
| 478 | |
| 479 reqData.taskType = taskType; | |
| 480 reqData.args = args; | |
| 481 reqData.taskID = taskID; | |
| 482 reqData.callingSlv = animSlv; | |
| 483 | |
| 484 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 485 } | |
| 486 | |
| 487 | |
| 488 /*This call is the last to happen in every task. It causes the slave to | |
| 489 * suspend and get the next task out of the task-queue. Notice there is no | |
| 490 * assigner here.. only one slave, no slave ReadyQ, and so on.. | |
| 491 *Can either make the assigner take the next task out of the taskQ, or can | |
| 492 * leave all as it is, and make task-end take the next task. | |
| 493 *Note: this fits the case in the new VMS for no-context tasks, so will use | |
| 494 * the built-in taskQ of new VMS, and should be local and much faster. | |
| 495 * | |
| 496 *The task-stub is saved in the animSlv, so the request handler will get it | |
| 497 * from there, along with the task-type which has arg types, and so on.. | |
| 498 * | |
| 499 * NOTE: if want, don't need to send the animating SlaveVP around.. | |
| 500 * instead, can make a single slave per core, and coreCtrlr looks up the | |
| 501 * slave from having the core number. | |
| 502 * | |
| 503 *But, to stay compatible with all the other VMS languages, leave it in.. | |
| 504 */ | |
| 505 void | |
| 506 VSs__end_task( SlaveVP *animSlv ) | |
| 507 { VSsSemReq reqData; | |
| 508 | |
| 509 reqData.reqType = end_task; | |
| 510 reqData.callingSlv = animSlv; | |
| 511 | |
| 512 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 513 } | |
| 514 | |
| 515 | |
| 516 void | |
| 517 VSs__taskwait(SlaveVP *animSlv) | |
| 518 { | |
| 519 VSsSemReq reqData; | |
| 520 | |
| 521 reqData.reqType = taskwait; | |
| 522 reqData.callingSlv = animSlv; | |
| 523 | |
| 524 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 525 } | |
| 526 | |
| 527 | |
| 528 | |
| 529 //========================== send and receive ============================ | |
| 530 // | |
| 531 | |
| 532 inline int32 * | |
| 533 VSs__give_self_taskID( SlaveVP *animSlv ) | |
| 534 { | |
| 535 return ((VSsSemData*)animSlv->semanticData)->taskStub->taskID; | |
| 536 } | |
| 537 | |
| 538 //================================ send =================================== | |
| 539 | |
| 540 void | |
| 541 VSs__send_of_type_to( void *msg, const int32 type, int32 *receiverID, | |
| 542 SlaveVP *senderSlv ) | |
| 543 { VSsSemReq reqData; | |
| 544 | |
| 545 reqData.reqType = send_type_to; | |
| 546 | |
| 547 reqData.msg = msg; | |
| 548 reqData.msgType = type; | |
| 549 reqData.receiverID = receiverID; | |
| 550 reqData.senderSlv = senderSlv; | |
| 551 | |
| 552 reqData.nextReqInHashEntry = NULL; | |
| 553 | |
| 554 VMS_WL__send_sem_request( &reqData, senderSlv ); | |
| 555 | |
| 556 //When come back from suspend, no longer own data reachable from msg | |
| 557 } | |
| 558 | |
| 559 void | |
| 560 VSs__send_from_to( void *msg, int32 *senderID, int32 *receiverID, SlaveVP *senderSlv ) | |
| 561 { VSsSemReq reqData; | |
| 562 | |
| 563 reqData.reqType = send_from_to; | |
| 564 | |
| 565 reqData.msg = msg; | |
| 566 reqData.senderID = senderID; | |
| 567 reqData.receiverID = receiverID; | |
| 568 reqData.senderSlv = senderSlv; | |
| 569 | |
| 570 reqData.nextReqInHashEntry = NULL; | |
| 571 | |
| 572 VMS_WL__send_sem_request( &reqData, senderSlv ); | |
| 573 } | |
| 574 | |
| 575 | |
| 576 //================================ receive ================================ | |
| 577 | |
| 578 /*The "type" version of send and receive creates a many-to-one relationship. | |
| 579 * The sender is anonymous, and many sends can stack up, waiting to be | |
| 580 * received. The same receiver can also have send from-to's | |
| 581 * waiting for it, and those will be kept separate from the "type" | |
| 582 * messages. | |
| 583 */ | |
| 584 void * | |
| 585 VSs__receive_type_to( const int32 type, int32* receiverID, SlaveVP *receiverSlv ) | |
| 586 { DEBUG__printf1(dbgRqstHdlr,"WL: receive type to %d",receiverID[1] ); | |
| 587 VSsSemReq reqData; | |
| 588 | |
| 589 reqData.reqType = receive_type_to; | |
| 590 | |
| 591 reqData.msgType = type; | |
| 592 reqData.receiverID = receiverID; | |
| 593 reqData.receiverSlv = receiverSlv; | |
| 594 | |
| 595 reqData.nextReqInHashEntry = NULL; | |
| 596 | |
| 597 VMS_WL__send_sem_request( &reqData, receiverSlv ); | |
| 598 | |
| 599 return receiverSlv->dataRetFromReq; | |
| 600 } | |
| 601 | |
| 602 | |
| 603 | |
| 604 /*Call this at the point a receiving task wants in-coming data. | |
| 605 * Use this from-to form when know senderID -- it makes a direct channel | |
| 606 * between sender and receiver. | |
| 607 */ | |
| 608 void * | |
| 609 VSs__receive_from_to( int32 *senderID, int32 *receiverID, SlaveVP *receiverSlv ) | |
| 610 { | |
| 611 VSsSemReq reqData; | |
| 612 | |
| 613 reqData.reqType = receive_from_to; | |
| 614 | |
| 615 reqData.senderID = senderID; | |
| 616 reqData.receiverID = receiverID; | |
| 617 reqData.receiverSlv = receiverSlv; | |
| 618 | |
| 619 reqData.nextReqInHashEntry = NULL; | |
| 620 DEBUG__printf2(dbgRqstHdlr,"WL: receive from %d to: %d", reqData.senderID[1], reqData.receiverID[1]); | |
| 621 | |
| 622 VMS_WL__send_sem_request( &reqData, receiverSlv ); | |
| 623 | |
| 624 return receiverSlv->dataRetFromReq; | |
| 625 } | |
| 626 | |
| 627 | |
| 628 | |
| 629 | |
| 630 //========================================================================== | |
| 631 // | |
| 632 /*A function singleton is a function whose body executes exactly once, on a | |
| 633 * single core, no matter how many times the fuction is called and no | |
| 634 * matter how many cores or the timing of cores calling it. | |
| 635 * | |
| 636 *A data singleton is a ticket attached to data. That ticket can be used | |
| 637 * to get the data through the function exactly once, no matter how many | |
| 638 * times the data is given to the function, and no matter the timing of | |
| 639 * trying to get the data through from different cores. | |
| 640 */ | |
| 641 | |
| 642 /*asm function declarations*/ | |
| 643 void asm_save_ret_to_singleton(VSsSingleton *singletonPtrAddr); | |
| 644 void asm_write_ret_from_singleton(VSsSingleton *singletonPtrAddr); | |
| 645 | |
| 646 /*Fn singleton uses ID as index into array of singleton structs held in the | |
| 647 * semantic environment. | |
| 648 */ | |
| 649 void | |
| 650 VSs__start_fn_singleton( int32 singletonID, SlaveVP *animSlv ) | |
| 651 { | |
| 652 VSsSemReq reqData; | |
| 653 | |
| 654 // | |
| 655 reqData.reqType = singleton_fn_start; | |
| 656 reqData.singletonID = singletonID; | |
| 657 | |
| 658 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 659 if( animSlv->dataRetFromReq ) //will be 0 or addr of label in end singleton | |
| 660 { | |
| 661 VSsSemEnv *semEnv = VMS_int__give_sem_env_for( animSlv ); | |
| 662 asm_write_ret_from_singleton(&(semEnv->fnSingletons[ singletonID])); | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 /*Data singleton hands addr of loc holding a pointer to a singleton struct. | |
| 667 * The start_data_singleton makes the structure and puts its addr into the | |
| 668 * location. | |
| 669 */ | |
| 670 void | |
| 671 VSs__start_data_singleton( VSsSingleton **singletonAddr, SlaveVP *animSlv ) | |
| 672 { | |
| 673 VSsSemReq reqData; | |
| 674 | |
| 675 if( *singletonAddr && (*singletonAddr)->hasFinished ) | |
| 676 goto JmpToEndSingleton; | |
| 677 | |
| 678 reqData.reqType = singleton_data_start; | |
| 679 reqData.singletonPtrAddr = singletonAddr; | |
| 680 | |
| 681 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 682 if( animSlv->dataRetFromReq ) //either 0 or end singleton's return addr | |
| 683 { //Assembly code changes the return addr on the stack to the one | |
| 684 // saved into the singleton by the end-singleton-fn | |
| 685 //The return addr is at 0x4(%%ebp) | |
| 686 JmpToEndSingleton: | |
| 687 asm_write_ret_from_singleton(*singletonAddr); | |
| 688 } | |
| 689 //now, simply return | |
| 690 //will exit either from the start singleton call or the end-singleton call | |
| 691 } | |
| 692 | |
| 693 /*Uses ID as index into array of flags. If flag already set, resumes from | |
| 694 * end-label. Else, sets flag and resumes normally. | |
| 695 * | |
| 696 *Note, this call cannot be inlined because the instr addr at the label | |
| 697 * inside is shared by all invocations of a given singleton ID. | |
| 698 */ | |
| 699 void | |
| 700 VSs__end_fn_singleton( int32 singletonID, SlaveVP *animSlv ) | |
| 701 { | |
| 702 VSsSemReq reqData; | |
| 703 | |
| 704 //don't need this addr until after at least one singleton has reached | |
| 705 // this function | |
| 706 VSsSemEnv *semEnv = VMS_int__give_sem_env_for( animSlv ); | |
| 707 asm_write_ret_from_singleton(&(semEnv->fnSingletons[ singletonID])); | |
| 708 | |
| 709 reqData.reqType = singleton_fn_end; | |
| 710 reqData.singletonID = singletonID; | |
| 711 | |
| 712 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 713 | |
| 714 EndSingletonInstrAddr: | |
| 715 return; | |
| 716 } | |
| 717 | |
| 718 void | |
| 719 VSs__end_data_singleton( VSsSingleton **singletonPtrAddr, SlaveVP *animSlv ) | |
| 720 { | |
| 721 VSsSemReq reqData; | |
| 722 | |
| 723 //don't need this addr until after singleton struct has reached | |
| 724 // this function for first time | |
| 725 //do assembly that saves the return addr of this fn call into the | |
| 726 // data singleton -- that data-singleton can only be given to exactly | |
| 727 // one instance in the code of this function. However, can use this | |
| 728 // function in different places for different data-singletons. | |
| 729 // (*(singletonAddr))->endInstrAddr = &&EndDataSingletonInstrAddr; | |
| 730 | |
| 731 | |
| 732 asm_save_ret_to_singleton(*singletonPtrAddr); | |
| 733 | |
| 734 reqData.reqType = singleton_data_end; | |
| 735 reqData.singletonPtrAddr = singletonPtrAddr; | |
| 736 | |
| 737 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 738 } | |
| 739 | |
| 740 /*This executes the function in the masterVP, so it executes in isolation | |
| 741 * from any other copies -- only one copy of the function can ever execute | |
| 742 * at a time. | |
| 743 * | |
| 744 *It suspends to the master, and the request handler takes the function | |
| 745 * pointer out of the request and calls it, then resumes the VP. | |
| 746 *Only very short functions should be called this way -- for longer-running | |
| 747 * isolation, use transaction-start and transaction-end, which run the code | |
| 748 * between as work-code. | |
| 749 */ | |
| 750 void | |
| 751 VSs__animate_short_fn_in_isolation( PtrToAtomicFn ptrToFnToExecInMaster, | |
| 752 void *data, SlaveVP *animSlv ) | |
| 753 { | |
| 754 VSsSemReq reqData; | |
| 755 | |
| 756 // | |
| 757 reqData.reqType = atomic; | |
| 758 reqData.fnToExecInMaster = ptrToFnToExecInMaster; | |
| 759 reqData.dataForFn = data; | |
| 760 | |
| 761 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 762 } | |
| 763 | |
| 764 | |
| 765 /*This suspends to the master. | |
| 766 *First, it looks at the VP's data, to see the highest transactionID that VP | |
| 767 * already has entered. If the current ID is not larger, it throws an | |
| 768 * exception stating a bug in the code. Otherwise it puts the current ID | |
| 769 * there, and adds the ID to a linked list of IDs entered -- the list is | |
| 770 * used to check that exits are properly ordered. | |
| 771 *Next it is uses transactionID as index into an array of transaction | |
| 772 * structures. | |
| 773 *If the "VP_currently_executing" field is non-null, then put requesting VP | |
| 774 * into queue in the struct. (At some point a holder will request | |
| 775 * end-transaction, which will take this VP from the queue and resume it.) | |
| 776 *If NULL, then write requesting into the field and resume. | |
| 777 */ | |
| 778 void | |
| 779 VSs__start_transaction( int32 transactionID, SlaveVP *animSlv ) | |
| 780 { | |
| 781 VSsSemReq reqData; | |
| 782 | |
| 783 // | |
| 784 reqData.callingSlv = animSlv; | |
| 785 reqData.reqType = trans_start; | |
| 786 reqData.transID = transactionID; | |
| 787 | |
| 788 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 789 } | |
| 790 | |
| 791 /*This suspends to the master, then uses transactionID as index into an | |
| 792 * array of transaction structures. | |
| 793 *It looks at VP_currently_executing to be sure it's same as requesting VP. | |
| 794 * If different, throws an exception, stating there's a bug in the code. | |
| 795 *Next it looks at the queue in the structure. | |
| 796 *If it's empty, it sets VP_currently_executing field to NULL and resumes. | |
| 797 *If something in, gets it, sets VP_currently_executing to that VP, then | |
| 798 * resumes both. | |
| 799 */ | |
| 800 void | |
| 801 VSs__end_transaction( int32 transactionID, SlaveVP *animSlv ) | |
| 802 { | |
| 803 VSsSemReq reqData; | |
| 804 | |
| 805 // | |
| 806 reqData.callingSlv = animSlv; | |
| 807 reqData.reqType = trans_end; | |
| 808 reqData.transID = transactionID; | |
| 809 | |
| 810 VMS_WL__send_sem_request( &reqData, animSlv ); | |
| 811 } | |
| 812 | |
| 813 //======================== Internal ================================== | |
| 814 /* | |
| 815 */ | |
| 816 SlaveVP * | |
| 817 VSs__create_slave_with( TopLevelFnPtr fnPtr, void *initData, | |
| 818 SlaveVP *creatingSlv ) | |
| 819 { VSsSemReq reqData; | |
| 820 | |
| 821 //the semantic request data is on the stack and disappears when this | |
| 822 // call returns -- it's guaranteed to remain in the VP's stack for as | |
| 823 // long as the VP is suspended. | |
| 824 reqData.reqType = 0; //know type because in a VMS create req | |
| 825 reqData.coreToAssignOnto = -1; //means round-robin assign | |
| 826 reqData.fnPtr = fnPtr; | |
| 827 reqData.initData = initData; | |
| 828 reqData.callingSlv = creatingSlv; | |
| 829 | |
| 830 VMS_WL__send_create_slaveVP_req( &reqData, creatingSlv ); | |
| 831 | |
| 832 return creatingSlv->dataRetFromReq; | |
| 833 } | |
| 834 | |
| 835 SlaveVP * | |
| 836 VSs__create_slave_with_affinity( TopLevelFnPtr fnPtr, void *initData, | |
| 837 SlaveVP *creatingSlv, int32 coreToAssignOnto ) | |
| 838 { VSsSemReq reqData; | |
| 839 | |
| 840 //the semantic request data is on the stack and disappears when this | |
| 841 // call returns -- it's guaranteed to remain in the VP's stack for as | |
| 842 // long as the VP is suspended. | |
| 843 reqData.reqType = create_slave_w_aff; //not used, May 2012 | |
| 844 reqData.coreToAssignOnto = coreToAssignOnto; | |
| 845 reqData.fnPtr = fnPtr; | |
| 846 reqData.initData = initData; | |
| 847 reqData.callingSlv = creatingSlv; | |
| 848 | |
| 849 VMS_WL__send_create_slaveVP_req( &reqData, creatingSlv ); | |
| 850 | |
| 851 return creatingSlv->dataRetFromReq; | |
| 852 } | |
| 853 |
