annotate VCilk_lib.c @ 8:e649c2387a60

new sequential version
author Merten Sach <msach@mailbox.tu-berlin.de>
date Thu, 02 Jun 2011 13:54:34 +0200
parents 58d0c2b1d6a4
children 5131f941f42c
rev   line source
msach@6 1 /*
msach@6 2 * Copyright 2010 OpenSourceCodeStewardshipFoundation
msach@6 3 *
msach@6 4 * Licensed under BSD
msach@6 5 */
msach@6 6
msach@6 7 #include <stdio.h>
msach@6 8 #include <stdlib.h>
msach@6 9
msach@6 10 #include "VMS/VMS.h"
msach@6 11 #include "VCilk.h"
msach@6 12 #include "VMS/Queue_impl/PrivateQueue.h"
msach@6 13 #include "VMS/Hash_impl/PrivateHash.h"
msach@6 14
msach@6 15
msach@6 16 //==========================================================================
msach@6 17
msach@6 18 void
msach@6 19 VCilk__init();
msach@6 20
msach@6 21 void
msach@6 22 VCilk__init_Seq();
msach@6 23
msach@6 24 void
msach@6 25 VCilk__init_Helper();
msach@6 26 //==========================================================================
msach@6 27
msach@6 28
msach@6 29 /*TODO: Q: dealing with library f()s and DKU vs WT vs FoR
msach@6 30 * (still want to do FoR, with time-lines as syntax, could be super cool)
msach@6 31 * A: thinking pin the coreLoops for all of BLIS -- let Master arbitrate
msach@6 32 * among library, DKU, WT, FoR -- all the patterns in terms of virtual
msach@6 33 * processors (or equivalently work-units), so Master picks which virt procr
msach@6 34 * from which portions of app (DKU, WT, FoR) onto which sched slots
msach@6 35 *Might even do hierarchy of masters -- group of sched slots for each core
msach@6 36 * has its own master, that keeps generated work local
msach@6 37 * single-reader-single-writer sync everywhere -- no atomic primitives (but
msach@6 38 * memory fences on architectures that need them)
msach@6 39 * Might have the different schedulers talk to each other, to negotiate
msach@6 40 * larger-grain sharing of resources, according to predicted critical
msach@6 41 * path, and expansion of work
msach@6 42 */
msach@6 43
msach@6 44
msach@6 45
msach@6 46 //===========================================================================
msach@6 47
msach@6 48
msach@6 49 /*These are the library functions *called in the application*
msach@6 50 *
msach@6 51 *There's a pattern for the outside sequential code to interact with the
msach@6 52 * VMS_HW code.
msach@6 53 *The VMS_HW system is inside a boundary.. every VCilk system is in its
msach@6 54 * own directory that contains the functions for each of the processor types.
msach@6 55 * One of the processor types is the "seed" processor that starts the
msach@6 56 * cascade of creating all the processors that do the work.
msach@6 57 *So, in the directory is a file called "EntryPoint.c" that contains the
msach@6 58 * function, named appropriately to the work performed, that the outside
msach@6 59 * sequential code calls. This function follows a pattern:
msach@6 60 *1) it calls VCilk__init()
msach@6 61 *2) it creates the initial data for the seed processor, which is passed
msach@6 62 * in to the function
msach@6 63 *3) it creates the seed VCilk processor, with the data to start it with.
msach@6 64 *4) it calls startVCilkThenWaitUntilWorkDone
msach@6 65 *5) it gets the returnValue from the transfer struc and returns that
msach@6 66 * from the function
msach@6 67 *
msach@6 68 *For now, a new VCilk system has to be created via VCilk__init every
msach@6 69 * time an entry point function is called -- later, might add letting the
msach@6 70 * VCilk system be created once, and let all the entry points just reuse
msach@6 71 * it -- want to be as simple as possible now, and see by using what makes
msach@6 72 * sense for later..
msach@6 73 */
msach@6 74
msach@6 75
msach@6 76
msach@6 77 //===========================================================================
msach@6 78
msach@6 79 /*This is the "border crossing" function -- the thing that crosses from the
msach@6 80 * outside world, into the VMS_HW world. It initializes and starts up the
msach@6 81 * VMS system, then creates one processor from the specified function and
msach@6 82 * puts it into the readyQ. From that point, that one function is resp.
msach@6 83 * for creating all the other processors, that then create others, and so
msach@6 84 * forth.
msach@6 85 *When all the processors, including the seed, have dissipated, then this
msach@6 86 * function returns. The results will have been written by side-effect via
msach@6 87 * pointers read from, or written into initData.
msach@6 88 *
msach@6 89 *NOTE: no Threads should exist in the outside program that might touch
msach@6 90 * any of the data reachable from initData passed in to here
msach@6 91 */
msach@6 92 void
msach@6 93 VCilk__create_seed_procr_and_do_work( VirtProcrFnPtr fnPtr, void *initData )
msach@6 94 { VCilkSemEnv *semEnv;
msach@6 95 VirtProcr *seedPr;
msach@6 96
msach@6 97 #ifdef SEQUENTIAL
msach@6 98 VCilk__init_Seq(); //debug sequential exe
msach@6 99 #else
msach@6 100 VCilk__init(); //normal multi-thd
msach@6 101 #endif
msach@6 102 semEnv = _VMSMasterEnv->semanticEnv;
msach@6 103
msach@6 104 //VCilk starts with one processor, which is put into initial environ,
msach@6 105 // and which then calls create() to create more, thereby expanding work
msach@6 106 seedPr = (VirtProcr*)VCilk__create_procr_helper( fnPtr, initData, NULL, semEnv, -1 );
msach@6 107 resume_procr( seedPr, semEnv );
msach@6 108
msach@6 109 #ifdef SEQUENTIAL
msach@6 110 VMS__start_the_work_then_wait_until_done_Seq(); //debug sequential exe
msach@6 111 #else
msach@6 112 VMS__start_the_work_then_wait_until_done(); //normal multi-thd
msach@6 113 #endif
msach@6 114
msach@6 115 VCilk__cleanup_at_end_of_shutdown();
msach@6 116 }
msach@6 117
msach@6 118
msach@6 119 int32 inline
msach@6 120 VCilk__giveMinWorkUnitCycles( float32 percentOverhead )
msach@6 121 {
msach@6 122 return MIN_WORK_UNIT_CYCLES;
msach@6 123 }
msach@6 124
msach@6 125 int32
msach@6 126 VCilk__giveIdealNumWorkUnits()
msach@6 127 {
msach@6 128 return NUM_SCHED_SLOTS * NUM_CORES;
msach@6 129 }
msach@6 130
msach@6 131 /*To measure how long a primitive operation takes, when calculating number of
msach@6 132 * sub-tasks to divide into.
msach@6 133 * For now, use TSC -- later, make these two macros with assembly that first
msach@6 134 * saves jump point, and second jumps back several times to get reliable time
msach@6 135 */
msach@6 136 void inline
msach@6 137 VCilk__start_primitive()
msach@6 138 { //int32 *saveAddr;
msach@6 139 //saveAddr = &(((VCilkSemEnv *)(_VMSMasterEnv->semanticEnv))->primitiveStartTime);
msach@6 140 saveLowTimeStampCountInto( (((VCilkSemEnv *)
msach@6 141 (_VMSMasterEnv->semanticEnv))->primitiveStartTime) );
msach@6 142 }
msach@6 143
msach@6 144 /*Just quick and dirty for now -- make reliable later
msach@6 145 * will want this to jump back several times -- to be sure cache is warm
msach@6 146 * because don't want comm time included in calc-time measurement -- and
msach@6 147 * also to throw out any "weird" values due to OS interrupt or TSC rollover
msach@6 148 */
msach@6 149 int32 inline
msach@6 150 VCilk__end_primitive_and_give_cycles()
msach@6 151 { int32 endTime, startTime;
msach@6 152 //TODO: fix by repeating time-measurement
msach@6 153 saveLowTimeStampCountInto( endTime );
msach@6 154 startTime = ( (VCilkSemEnv *)
msach@6 155 (_VMSMasterEnv->semanticEnv))->primitiveStartTime;
msach@6 156 return (endTime - startTime);
msach@6 157 }
msach@6 158
msach@6 159 //===========================================================================
msach@6 160 //
msach@6 161 /*Initializes all the data-structures for a VCilk system -- but doesn't
msach@6 162 * start it running yet!
msach@6 163 *
msach@6 164 *This and its callees run in main thread outside VMS
msach@6 165 *
msach@6 166 *This sets up the semantic layer over the VMS system
msach@6 167 *
msach@6 168 *First, calls VMS_Setup, then creates own environment, making it ready
msach@6 169 * for creating the seed processor and then starting the work.
msach@6 170 */
msach@6 171 void
msach@6 172 VCilk__init()
msach@6 173 {
msach@6 174 VMS__init();
msach@6 175 //masterEnv, a global var, now is partially set up by init_VMS
msach@6 176
msach@6 177 VCilk__init_Helper();
msach@6 178 }
msach@6 179
msach@8 180 #ifdef SEQUENTIAL
msach@6 181 void
msach@6 182 VCilk__init_Seq()
msach@6 183 {
msach@6 184 VMS__init_Seq();
msach@6 185 //masterEnv, a global var, now is partially set up by init_VMS
msach@6 186
msach@6 187 VCilk__init_Helper();
msach@6 188 }
msach@8 189 #endif
msach@6 190
msach@6 191 /*Runs in main thread before VMS system starts
msach@6 192 */
msach@6 193 void
msach@6 194 VCilk__init_Helper()
msach@6 195 { VCilkSemEnv *semanticEnv;
msach@6 196 PrivQueueStruc **readyVPQs;
msach@6 197 int coreIdx;
msach@6 198
msach@6 199 //Hook up the semantic layer's plug-ins to the Master virt procr
msach@6 200 _VMSMasterEnv->requestHandler = &VCilk__Request_Handler;
msach@6 201 _VMSMasterEnv->slaveScheduler = &VCilk__schedule_virt_procr;
msach@6 202
msach@6 203 //create the semantic layer's environment (all its data) and add to
msach@6 204 // the master environment
msach@6 205 semanticEnv = VMS__malloc( sizeof( VCilkSemEnv ) );
msach@6 206 _VMSMasterEnv->semanticEnv = semanticEnv;
msach@6 207
msach@6 208 //create the ready queue, hash tables used for pairing send to receive
msach@6 209 // and so forth
msach@6 210 //TODO: add hash tables for pairing sends with receives, and
msach@6 211 // initialize the data ownership system
msach@6 212 readyVPQs = VMS__malloc( NUM_CORES * sizeof(PrivQueueStruc *) );
msach@6 213
msach@6 214 for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ )
msach@6 215 {
msach@6 216 readyVPQs[ coreIdx ] = makeVMSPrivQ();
msach@6 217 }
msach@6 218
msach@6 219 semanticEnv->readyVPQs = readyVPQs;
msach@6 220
msach@6 221 semanticEnv->nextCoreToGetNewPr = 0;
msach@6 222
msach@6 223 //TODO: bug -- turn these arrays into dyn arrays to eliminate limit
msach@6 224 //semanticEnv->singletonHasBeenExecutedFlags = makeDynArrayInfo( );
msach@6 225 //semanticEnv->transactionStrucs = makeDynArrayInfo( );
msach@6 226 //something like: setHighestIdx( dynArrayInfo, NUM_STRUCS_IN_SEM_ENV )
msach@6 227 int32 i;
msach@6 228 for( i = 0; i < NUM_STRUCS_IN_SEM_ENV; i++ )
msach@6 229 {
msach@6 230 semanticEnv->fnSingletons[i].endInstrAddr = NULL;
msach@6 231 semanticEnv->fnSingletons[i].hasBeenStarted = FALSE;
msach@6 232 semanticEnv->fnSingletons[i].hasFinished = FALSE;
msach@6 233 semanticEnv->fnSingletons[i].waitQ = makeVMSPrivQ();
msach@6 234 semanticEnv->transactionStrucs[i].waitingVPQ = makeVMSPrivQ();
msach@6 235 }
msach@6 236
msach@6 237 }
msach@6 238
msach@6 239
msach@6 240 /*Runs in main thread, outside VMS
msach@6 241 *Frees any memory allocated by VCilk__init() then calls VMS's cleanup
msach@6 242 */
msach@6 243 void
msach@6 244 VCilk__cleanup_at_end_of_shutdown()
msach@6 245 { VCilkSemEnv *semanticEnv;
msach@6 246
msach@6 247 semanticEnv = _VMSMasterEnv->semanticEnv;
msach@6 248
msach@6 249 /*
msach@6 250 int32 coreIdx;
msach@6 251 for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ )
msach@6 252 {
msach@6 253 VMS__free( semanticEnv->readyVPQs[coreIdx]->startOfData );
msach@6 254 VMS__free( semanticEnv->readyVPQs[coreIdx] );
msach@6 255 }
msach@6 256 VMS__free( semanticEnv->readyVPQs );
msach@6 257
msach@6 258 VMS__free( _VMSMasterEnv->semanticEnv );
msach@6 259 */
msach@6 260 VMS__cleanup_at_end_of_shutdown();
msach@6 261 }
msach@6 262
msach@6 263
msach@6 264 //===========================================================================
msach@6 265
msach@6 266
msach@6 267 /*Spawn involves allocating mem as well as creating processor which itself
msach@6 268 * allocates, so has to be done inside master
msach@6 269 */
msach@6 270 void inline
msach@6 271 VCilk__spawn( int32 coreToSpawnOnto, VirtProcrFnPtr fnPtr,
msach@6 272 void *initData, VirtProcr *requestingPr )
msach@6 273 { VCilkSemReq reqData;
msach@6 274
msach@6 275 //the semantic request data is on the stack and disappears when this
msach@6 276 // call returns -- it's guaranteed to remain in the VP's stack for as
msach@6 277 // long as the VP is suspended.
msach@6 278 reqData.reqType = 0; //know it's type because in a VMS create req
msach@6 279 reqData.coreToSpawnOnto = coreToSpawnOnto;
msach@6 280 reqData.fnPtr = fnPtr;
msach@6 281 reqData.initData = initData;
msach@6 282 reqData.requestingPr = requestingPr;
msach@6 283
msach@6 284 VMS__send_create_procr_req( &reqData, requestingPr );
msach@6 285 }
msach@6 286
msach@6 287
msach@6 288 int32
msach@6 289 VCilk__give_number_of_cores_to_spawn_onto()
msach@6 290 {
msach@6 291 return NUM_CORES;
msach@6 292 }
msach@6 293
msach@6 294
msach@6 295
msach@6 296 /*This runs inside slave VP, so can't do any freeing -- have to do in plugin
msach@6 297 */
msach@6 298 void inline
msach@6 299 VCilk__dissipate_procr( VirtProcr *procrToDissipate )
msach@6 300 {
msach@6 301
msach@6 302 VMS__send_dissipate_req( procrToDissipate );
msach@6 303 }
msach@6 304
msach@6 305 //===========================================================================
msach@6 306
msach@6 307 void
msach@6 308 VCilk__sync( VirtProcr *animPr )
msach@6 309 { VCilkSemReq reqData;
msach@6 310
msach@6 311 reqData.reqType = syncReq;
msach@6 312 reqData.requestingPr = animPr;
msach@6 313
msach@6 314 VMS__send_sem_request( &reqData, animPr );
msach@6 315 }
msach@6 316
msach@6 317
msach@6 318
msach@6 319 void *
msach@6 320 VCilk__malloc( int32 sizeToMalloc, VirtProcr *animPr )
msach@6 321 { VCilkSemReq reqData;
msach@6 322
msach@6 323 reqData.reqType = mallocReq;
msach@6 324 reqData.requestingPr = animPr;
msach@6 325 reqData.sizeToMalloc = sizeToMalloc;
msach@6 326
msach@6 327 VMS__send_sem_request( &reqData, animPr );
msach@6 328
msach@6 329 return animPr->dataRetFromReq;
msach@6 330 }
msach@6 331
msach@6 332
msach@6 333 /*Sends request to Master, which does the work of freeing
msach@6 334 */
msach@6 335 void
msach@6 336 VCilk__free( void *ptrToFree, VirtProcr *animPr )
msach@6 337 { VCilkSemReq reqData;
msach@6 338
msach@6 339 reqData.reqType = freeReq;
msach@6 340 reqData.requestingPr = animPr;
msach@6 341 reqData.ptrToFree = ptrToFree;
msach@6 342
msach@6 343 VMS__send_sem_request( &reqData, animPr );
msach@6 344 }
msach@6 345
msach@6 346 //===========================================================================
msach@6 347 //
msach@6 348 /*A function singleton is a function whose body executes exactly once, on a
msach@6 349 * single core, no matter how many times the fuction is called and no
msach@6 350 * matter how many cores or the timing of cores calling it.
msach@6 351 *
msach@6 352 *A data singleton is a ticket attached to data. That ticket can be used
msach@6 353 * to get the data through the function exactly once, no matter how many
msach@6 354 * times the data is given to the function, and no matter the timing of
msach@6 355 * trying to get the data through from different cores.
msach@6 356 */
msach@6 357
msach@6 358 /*Fn singleton uses ID as index into array of singleton structs held in the
msach@6 359 * semantic environment.
msach@6 360 */
msach@6 361 void
msach@6 362 VCilk__start_fn_singleton( int32 singletonID, VirtProcr *animPr )
msach@6 363 {
msach@6 364 VCilkSemReq reqData;
msach@6 365
msach@6 366 //
msach@6 367 reqData.reqType = singleton_fn_start;
msach@6 368 reqData.singletonID = singletonID;
msach@6 369
msach@6 370 VMS__send_sem_request( &reqData, animPr );
msach@6 371 if( animPr->dataRetFromReq ) //will be 0 or addr of label in end singleton
msach@6 372 {
msach@6 373 asm volatile("movl %0, %%eax; \
msach@6 374 jmp *%%eax" \
msach@6 375 /* outputs */ : \
msach@6 376 /* inputs */ : "g"(animPr->dataRetFromReq) \
msach@6 377 /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx","%edi","%esi"\
msach@6 378 );
msach@6 379 }
msach@6 380 }
msach@6 381
msach@6 382 /*Data singleton hands addr of loc holding a pointer to a singleton struct.
msach@6 383 * The start_data_singleton makes the structure and puts its addr into the
msach@6 384 * location.
msach@6 385 */
msach@6 386 void
msach@6 387 VCilk__start_data_singleton( VCilkSingleton **singletonAddr, VirtProcr *animPr )
msach@6 388 {
msach@6 389 VCilkSemReq reqData;
msach@6 390
msach@6 391 if( *singletonAddr && (*singletonAddr)->hasFinished )
msach@6 392 goto JmpToEndSingleton;
msach@6 393 //
msach@6 394 reqData.reqType = singleton_data_start;
msach@6 395 reqData.singletonPtrAddr = singletonAddr;
msach@6 396
msach@6 397 VMS__send_sem_request( &reqData, animPr );
msach@6 398 if( animPr->dataRetFromReq ) //either 0 or end singleton's return addr
msach@6 399 { //Assembly code changes the return addr on the stack to the one
msach@6 400 // saved into the singleton by the end-singleton-fn
msach@6 401 //The return addr is at 0x4(%%ebp)
msach@6 402 JmpToEndSingleton:
msach@6 403 asm volatile("movl %0, %%eax; \
msach@6 404 movl (%%eax), %%ebx; \
msach@6 405 movl (%%ebx), %%eax; \
msach@6 406 movl %%eax, 0x4(%%ebp);" \
msach@6 407 /* outputs */ : \
msach@6 408 /* inputs */ : "m"(singletonAddr) \
msach@6 409 /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx","%edi","%esi"\
msach@6 410 );
msach@6 411 }
msach@6 412 //now, simply return
msach@6 413 //will exit either from the start singleton call or the end-singleton call
msach@6 414 }
msach@6 415
msach@6 416 /*Uses ID as index into array of flags. If flag already set, resumes from
msach@6 417 * end-label. Else, sets flag and resumes normally.
msach@6 418 *
msach@6 419 *Note, this call cannot be inlined because the instr addr at the label
msach@6 420 * inside is shared by all invocations of a given singleton ID.
msach@6 421 */
msach@6 422 void
msach@6 423 VCilk__end_fn_singleton( int32 singletonID, VirtProcr *animPr )
msach@6 424 {
msach@6 425 VCilkSemReq reqData;
msach@6 426
msach@6 427 //don't need this addr until after at least one singleton has reached
msach@6 428 // this function
msach@6 429 VCilkSemEnv *semEnv = VMS__give_sem_env_for( animPr );
msach@6 430 semEnv->fnSingletons[ singletonID].endInstrAddr = &&EndSingletonInstrAddr;
msach@6 431
msach@6 432 reqData.reqType = singleton_fn_end;
msach@6 433 reqData.singletonID = singletonID;
msach@6 434
msach@6 435 VMS__send_sem_request( &reqData, animPr );
msach@6 436
msach@6 437 EndSingletonInstrAddr:
msach@6 438 return;
msach@6 439 }
msach@6 440
msach@6 441 void
msach@6 442 VCilk__end_data_singleton( VCilkSingleton **singletonPtrAddr, VirtProcr *animPr )
msach@6 443 {
msach@6 444 VCilkSemReq reqData;
msach@6 445
msach@6 446 //don't need this addr until after singleton struct has reached
msach@6 447 // this function for first time
msach@6 448 //do assembly that saves the return addr of this fn call into the
msach@6 449 // data singleton -- that data-singleton can only be given to exactly
msach@6 450 // one instance in the code of this function. However, can use this
msach@6 451 // function in different places for different data-singletons.
msach@6 452 // (*(singletonAddr))->endInstrAddr = &&EndDataSingletonInstrAddr;
msach@6 453
msach@6 454 //Assembly code takes the return addr off the stack and saves
msach@6 455 // into the singleton. The first field in the singleton is the
msach@6 456 // "endInstrAddr" field, and the return addr is at 0x4(%%ebp)
msach@6 457 asm volatile("movl 0x4(%%ebp), %%eax; \
msach@6 458 movl %0, %%ebx; \
msach@6 459 movl (%%ebx), %%ecx; \
msach@6 460 movl %%eax, (%%ecx);" \
msach@6 461 /* outputs */ : \
msach@6 462 /* inputs */ : "m"(singletonPtrAddr) \
msach@6 463 /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx","%edi","%esi"\
msach@6 464 );
msach@6 465
msach@6 466 reqData.reqType = singleton_data_end;
msach@6 467 reqData.singletonPtrAddr = singletonPtrAddr;
msach@6 468
msach@6 469 VMS__send_sem_request( &reqData, animPr );
msach@6 470 }
msach@6 471
msach@6 472 /*This executes the function in the masterVP, so it executes in isolation
msach@6 473 * from any other copies -- only one copy of the function can ever execute
msach@6 474 * at a time.
msach@6 475 *
msach@6 476 *It suspends to the master, and the request handler takes the function
msach@6 477 * pointer out of the request and calls it, then resumes the VP.
msach@6 478 *Only very short functions should be called this way -- for longer-running
msach@6 479 * isolation, use transaction-start and transaction-end, which run the code
msach@6 480 * between as work-code.
msach@6 481 */
msach@6 482 void
msach@6 483 VCilk__animate_short_fn_in_isolation( PtrToAtomicFn ptrToFnToExecInMaster,
msach@6 484 void *data, VirtProcr *animPr )
msach@6 485 {
msach@6 486 VCilkSemReq reqData;
msach@6 487
msach@6 488 //
msach@6 489 reqData.reqType = atomic;
msach@6 490 reqData.fnToExecInMaster = ptrToFnToExecInMaster;
msach@6 491 reqData.dataForFn = data;
msach@6 492
msach@6 493 VMS__send_sem_request( &reqData, animPr );
msach@6 494 }
msach@6 495
msach@6 496
msach@6 497 /*This suspends to the master.
msach@6 498 *First, it looks at the VP's data, to see the highest transactionID that VP
msach@6 499 * already has entered. If the current ID is not larger, it throws an
msach@6 500 * exception stating a bug in the code. Otherwise it puts the current ID
msach@6 501 * there, and adds the ID to a linked list of IDs entered -- the list is
msach@6 502 * used to check that exits are properly ordered.
msach@6 503 *Next it is uses transactionID as index into an array of transaction
msach@6 504 * structures.
msach@6 505 *If the "VP_currently_executing" field is non-null, then put requesting VP
msach@6 506 * into queue in the struct. (At some point a holder will request
msach@6 507 * end-transaction, which will take this VP from the queue and resume it.)
msach@6 508 *If NULL, then write requesting into the field and resume.
msach@6 509 */
msach@6 510 void
msach@6 511 VCilk__start_transaction( int32 transactionID, VirtProcr *animPr )
msach@6 512 {
msach@6 513 VCilkSemReq reqData;
msach@6 514
msach@6 515 //
msach@6 516 reqData.reqType = trans_start;
msach@6 517 reqData.transID = transactionID;
msach@6 518
msach@6 519 VMS__send_sem_request( &reqData, animPr );
msach@6 520 }
msach@6 521
msach@6 522 /*This suspends to the master, then uses transactionID as index into an
msach@6 523 * array of transaction structures.
msach@6 524 *It looks at VP_currently_executing to be sure it's same as requesting VP.
msach@6 525 * If different, throws an exception, stating there's a bug in the code.
msach@6 526 *Next it looks at the queue in the structure.
msach@6 527 *If it's empty, it sets VP_currently_executing field to NULL and resumes.
msach@6 528 *If something in, gets it, sets VP_currently_executing to that VP, then
msach@6 529 * resumes both.
msach@6 530 */
msach@6 531 void
msach@6 532 VCilk__end_transaction( int32 transactionID, VirtProcr *animPr )
msach@6 533 {
msach@6 534 VCilkSemReq reqData;
msach@6 535
msach@6 536 //
msach@6 537 reqData.reqType = trans_end;
msach@6 538 reqData.transID = transactionID;
msach@6 539
msach@6 540 VMS__send_sem_request( &reqData, animPr );
msach@6 541 }