# HG changeset patch # User Me # Date 1290238798 -3600 # Node ID 505d3c674ce884c84bcd01d0210bc0481912cb33 # Parent e960a8d18f7c34146c78e8c8ae6f00dcc09472bb Nov 20 PLDI final version -- working data singletons, meas hist macros in plugin diff -r e960a8d18f7c -r 505d3c674ce8 VPThread.h --- a/VPThread.h Tue Nov 16 16:04:29 2010 +0100 +++ b/VPThread.h Sat Nov 20 08:39:58 2010 +0100 @@ -31,6 +31,17 @@ //=========================================================================== +/*WARNING: assembly hard-codes position of endInstrAddr as first field + */ +typedef struct + { + void *endInstrAddr; + int32 hasBeenStarted; + int32 hasFinished; + PrivQueueStruc *waitQ; + } +VPThdSingleton; + /*Semantic layer-specific data sent inside a request from lib called in app * to request handler called in MasterLoop */ @@ -45,8 +56,10 @@ make_procr, malloc_req, free_req, - singleton_start, - singleton_end, + singleton_fn_start, + singleton_fn_end, + singleton_data_start, + singleton_data_end, atomic, trans_start, trans_end @@ -66,7 +79,7 @@ void *ptrToFree; int32 singletonID; - void *endJumpPt; + VPThdSingleton **singletonPtrAddr; PtrToAtomicFn fnToExecInMaster; void *dataForFn; @@ -83,14 +96,6 @@ } VPThdTrans; -typedef struct - { - int32 hasBeenStarted; - int32 hasFinished; - void *endInstrAddr; - PrivQueueStruc *waitQ; - } -VPThdSingleton; typedef struct { @@ -143,8 +148,7 @@ void *applicationGlobals; //fix limit on num with dynArray - VPThdSingleton singletons[NUM_STRUCS_IN_SEM_ENV]; - void *singletonEndInstrAddr; + VPThdSingleton fnSingletons[NUM_STRUCS_IN_SEM_ENV]; VPThdTrans transactionStrucs[NUM_STRUCS_IN_SEM_ENV]; } @@ -199,12 +203,17 @@ //======================= +void +VPThread__start_fn_singleton( int32 singletonID, VirtProcr *animPr ); void -VPThread__end_singleton( int32 singletonID, VirtProcr *animPr ); +VPThread__end_fn_singleton( int32 singletonID, VirtProcr *animPr ); -inline void -VPThread__start_singleton( int32 singletonID, VirtProcr *animPr ); +void +VPThread__start_data_singleton( VPThdSingleton **singeltonAddr, VirtProcr *animPr ); + +void +VPThread__end_data_singleton( VPThdSingleton **singletonAddr, VirtProcr *animPr ); void VPThread__animate_short_fn_in_isolation( PtrToAtomicFn ptrToFnToExecInMaster, @@ -237,5 +246,8 @@ void VPThread__cleanup_after_shutdown(); +void inline +resume_procr( VirtProcr *procr, VPThdSemEnv *semEnv ); + #endif /* _VPThread_H */ diff -r e960a8d18f7c -r 505d3c674ce8 VPThread_PluginFns.c --- a/VPThread_PluginFns.c Tue Nov 16 16:04:29 2010 +0100 +++ b/VPThread_PluginFns.c Sat Nov 20 08:39:58 2010 +0100 @@ -13,16 +13,14 @@ #include "VPThread_Request_Handlers.h" //=========================== Local Fn Prototypes =========================== -void inline -resume_procr( VirtProcr *procr, VPThdSemEnv *semEnv ); void inline handleSemReq( VMSReqst *req, VirtProcr *requestingPr, VPThdSemEnv *semEnv ); -void +inline void handleDissipate( VirtProcr *requestingPr, VPThdSemEnv *semEnv ); -void +inline void handleCreate( VMSReqst *req, VirtProcr *requestingPr, VPThdSemEnv *semEnv ); @@ -111,12 +109,30 @@ break; case cond_signal: handleCondSignal( semReq, semEnv); break; + case malloc_req: handleMalloc( semReq, reqPr, semEnv); + break; + case free_req: handleFree( semReq, reqPr, semEnv); + break; + case singleton_fn_start: handleStartFnSingleton(semReq, reqPr, semEnv); + break; + case singleton_fn_end: handleEndFnSingleton( semReq, reqPr, semEnv); + break; + case singleton_data_start:handleStartDataSingleton(semReq,reqPr,semEnv); + break; + case singleton_data_end: handleEndDataSingleton(semReq, reqPr, semEnv); + break; + case atomic: handleAtomic( semReq, reqPr, semEnv); + break; + case trans_start: handleTransStart( semReq, reqPr, semEnv); + break; + case trans_end: handleTransEnd( semReq, reqPr, semEnv); + break; } } //=========================== VMS Request Handlers =========================== // -void +inline void handleDissipate( VirtProcr *requestingPr, VPThdSemEnv *semEnv ) { //free any semantic data allocated to the virt procr @@ -137,8 +153,8 @@ inline VirtProcr * VPThread__create_procr_helper( VirtProcrFnPtr fnPtr, void *initData, VPThdSemEnv *semEnv, int32 coreToScheduleOnto ) - { VirtProcr *newPr; - VPThdSemData semData; + { VirtProcr *newPr; + VPThdSemData *semData; //This is running in master, so use internal version newPr = VMS__create_procr( fnPtr, initData ); @@ -175,11 +191,13 @@ return newPr; } -void +inline void handleCreate( VMSReqst *req, VirtProcr *requestingPr, VPThdSemEnv *semEnv ) { VPThdSemReq *semReq; VirtProcr *newPr; + Meas_startCreate + semReq = VMS__take_sem_reqst_from( req ); newPr = VPThread__create_procr_helper( semReq->fnPtr, semReq->initData, @@ -190,6 +208,16 @@ resume_procr( newPr, semEnv ); resume_procr( requestingPr, semEnv ); + + //========================= MEASUREMENT STUFF ====================== + Meas_endCreate + #ifdef MEAS__TIME_PLUGIN + #ifdef MEAS__SUB_CREATE + subIntervalFromHist( startStamp, endStamp, + _VMSMasterEnv->reqHdlrHighTimeHist ); + #endif + #endif + //================================================================== } diff -r e960a8d18f7c -r 505d3c674ce8 VPThread_Request_Handlers.c --- a/VPThread_Request_Handlers.c Tue Nov 16 16:04:29 2010 +0100 +++ b/VPThread_Request_Handlers.c Sat Nov 20 08:39:58 2010 +0100 @@ -24,8 +24,8 @@ VirtProcr *requestingPr; requestingPr = semReq->requestingPr; - newMutex = VMS__malloc( sizeof(VPThdMutex), requestingPr ); - newMutex->waitingQueue = makePrivQ( requestingPr ); + newMutex = VMS__malloc( sizeof(VPThdMutex) ); + newMutex->waitingQueue = makeVMSPrivQ( requestingPr ); newMutex->holderOfLock = NULL; //The mutex struc contains an int that identifies it -- use that as @@ -36,7 +36,7 @@ semReq->requestingPr->dataRetFromReq = newMutex->mutexIdx; //re-animate the requester - resume_procr( requestingPr ); + resume_procr( requestingPr, semEnv ); } @@ -49,6 +49,7 @@ #endif //================================================================= + Meas_startMutexLock //lookup mutex struc, using mutexIdx as index mutex = semEnv->mutexDynArray[ semReq->mutexIdx ]; @@ -58,12 +59,13 @@ mutex->holderOfLock = semReq->requestingPr; //re-animate requester, now that it has the lock - resume_procr( semReq->requestingPr ); + resume_procr( semReq->requestingPr, semEnv ); } else //queue up requester to wait for release of lock { writePrivQ( semReq->requestingPr, mutex->waitingQueue ); } + Meas_endMutexLock } /* @@ -72,6 +74,7 @@ handleMutexUnlock( VPThdSemReq *semReq, VPThdSemEnv *semEnv) { VPThdMutex *mutex; + Meas_startMutexUnlock //lookup mutex struc, using mutexIdx as index mutex = semEnv->mutexDynArray[ semReq->mutexIdx ]; @@ -81,11 +84,12 @@ //if have new non-NULL holder, re-animate it if( mutex->holderOfLock != NULL ) { - resume_procr( mutex->holderOfLock ); + resume_procr( mutex->holderOfLock, semEnv ); } //re-animate the releaser of the lock - resume_procr( semReq->requestingPr ); + resume_procr( semReq->requestingPr, semEnv ); + Meas_endMutexUnlock } //=========================== Condition Vars ============================== @@ -106,10 +110,10 @@ VirtProcr *requestingPr; requestingPr = semReq->requestingPr; - newCond = VMS__malloc( sizeof(VPThdCond), requestingPr ); + newCond = VMS__malloc( sizeof(VPThdCond) ); newCond->partnerMutex = semEnv->mutexDynArray[ semReq->mutexIdx ]; - newCond->waitingQueue = makePrivQ(); + newCond->waitingQueue = makeVMSPrivQ(); //The cond struc contains an int that identifies it -- use that as // its index within the array of conds. Add the new cond to array. @@ -119,7 +123,7 @@ semReq->requestingPr->dataRetFromReq = newCond->condIdx; //re-animate the requester - resume_procr( requestingPr ); + resume_procr( requestingPr, semEnv ); } @@ -131,8 +135,8 @@ handleCondWait( VPThdSemReq *semReq, VPThdSemEnv *semEnv) { VPThdCond *cond; VPThdMutex *mutex; - VirtProcr *pr; + Meas_startCondWait //get cond struc out of array of them that's in the sem env cond = semEnv->condDynArray[ semReq->condIdx ]; @@ -145,8 +149,9 @@ if( mutex->holderOfLock != NULL ) { - resume_procr( mutex->holderOfLock ); + resume_procr( mutex->holderOfLock, semEnv ); } + Meas_endCondWait } @@ -159,6 +164,7 @@ VPThdMutex *mutex; VirtProcr *waitingPr, *pr; + Meas_startCondSignal //get cond struc out of array of them that's in the sem env cond = semEnv->condDynArray[ semReq->condIdx ]; @@ -171,7 +177,8 @@ pushPrivQ( waitingPr, mutex->waitingQueue ); //is first out when read //re-animate the signalling procr - resume_procr( semReq->requestingPr ); + resume_procr( semReq->requestingPr, semEnv ); + Meas_endCondSignal } @@ -184,73 +191,147 @@ handleMalloc(VPThdSemReq *semReq, VirtProcr *requestingPr,VPThdSemEnv *semEnv) { void *ptr; + //========================= MEASUREMENT STUFF ====================== + #ifdef MEAS__TIME_PLUGIN + int32 startStamp, endStamp; + saveLowTimeStampCountInto( startStamp ); + #endif + //================================================================== ptr = VMS__malloc( semReq->sizeToMalloc ); requestingPr->dataRetFromReq = ptr; resume_procr( requestingPr, semEnv ); - } + //========================= MEASUREMENT STUFF ====================== + #ifdef MEAS__TIME_PLUGIN + saveLowTimeStampCountInto( endStamp ); + subIntervalFromHist( startStamp, endStamp, + _VMSMasterEnv->reqHdlrHighTimeHist ); + #endif + //================================================================== + } /* */ void inline handleFree( VPThdSemReq *semReq, VirtProcr *requestingPr, VPThdSemEnv *semEnv) { + //========================= MEASUREMENT STUFF ====================== + #ifdef MEAS__TIME_PLUGIN + int32 startStamp, endStamp; + saveLowTimeStampCountInto( startStamp ); + #endif + //================================================================== VMS__free( semReq->ptrToFree ); resume_procr( requestingPr, semEnv ); + //========================= MEASUREMENT STUFF ====================== + #ifdef MEAS__TIME_PLUGIN + saveLowTimeStampCountInto( endStamp ); + subIntervalFromHist( startStamp, endStamp, + _VMSMasterEnv->reqHdlrHighTimeHist ); + #endif + //================================================================== } -//============================================================================ +//=========================================================================== // /*Uses ID as index into array of flags. If flag already set, resumes from * end-label. Else, sets flag and resumes normally. - *Note, because of one-master-at-a-time, if flag is set, */ void inline -handleStartSingleton( VPThdSemReq *semReq, VirtProcr *reqstingPr, - VPThdSemEnv *semEnv ) +handleStartSingleton_helper( VPThdSingleton *singleton, VirtProcr *reqstingPr, + VPThdSemEnv *semEnv ) { - if( semEnv->singletons[ semReq->singletonID ].hasFinished ) + if( singleton->hasFinished ) { //the code that sets the flag to true first sets the end instr addr - reqstingPr->nextInstrPt = semEnv->singletonEndInstrAddr; + reqstingPr->dataRetFromReq = singleton->endInstrAddr; resume_procr( reqstingPr, semEnv ); return; } - else if( semEnv->singletons[ semReq->singletonID ].hasBeenStarted ) + else if( singleton->hasBeenStarted ) { //singleton is in-progress in a diff slave, so wait for it to finish - writePrivQ(reqstingPr, semEnv->singletons[semReq->singletonID].waitQ ); + writePrivQ(reqstingPr, singleton->waitQ ); return; } else { //hasn't been started, so this is the first attempt at the singleton - semEnv->singletons[ semReq->singletonID ].hasBeenStarted = TRUE; + singleton->hasBeenStarted = TRUE; + reqstingPr->dataRetFromReq = 0x0; resume_procr( reqstingPr, semEnv ); return; } } +void inline +handleStartFnSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, + VPThdSemEnv *semEnv ) + { VPThdSingleton *singleton; + + singleton = &(semEnv->fnSingletons[ semReq->singletonID ]); + handleStartSingleton_helper( singleton, requestingPr, semEnv ); + } +void inline +handleStartDataSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, + VPThdSemEnv *semEnv ) + { VPThdSingleton *singleton; + + if( *(semReq->singletonPtrAddr) == NULL ) + { singleton = VMS__malloc( sizeof(VPThdSingleton) ); + singleton->waitQ = makeVMSPrivQ(); + singleton->endInstrAddr = 0x0; + singleton->hasBeenStarted = FALSE; + singleton->hasFinished = FALSE; + *(semReq->singletonPtrAddr) = singleton; + } + else + singleton = *(semReq->singletonPtrAddr); + handleStartSingleton_helper( singleton, requestingPr, semEnv ); + } + void inline -handleEndSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, - VPThdSemEnv *semEnv ) - { - PrivQueueStruc waitQ; - int32 numWaiting, i; - - if( semEnv->singletons[ semReq->singletonID ].hasFinished ) +handleEndSingleton_helper( VPThdSingleton *singleton, VirtProcr *requestingPr, + VPThdSemEnv *semEnv ) + { PrivQueueStruc *waitQ; + int32 numWaiting, i; + VirtProcr *resumingPr; + + if( singleton->hasFinished ) { //by definition, only one slave should ever be able to run end singleton // so if this is true, is an error - VMS__throw_exception( "singleton code ran twice"); + //VMS__throw_exception( "singleton code ran twice", requestingPr, NULL); } - semEnv->singletons[ semReq->singletonID ].hasFinished = TRUE; - waitQ = semEnv->singletons[ semReq->singletonID ].waitQ; + singleton->hasFinished = TRUE; + waitQ = singleton->waitQ; numWaiting = numInPrivQ( waitQ ); for( i = 0; i < numWaiting; i++ ) - { - resume_procr( readPrivQ( waitQ ) ); + { //they will resume inside start singleton, then jmp to end singleton + resumingPr = readPrivQ( waitQ ); + resumingPr->dataRetFromReq = singleton->endInstrAddr; + resume_procr( resumingPr, semEnv ); } - - resume_procr( requestingPr ); - } + + resume_procr( requestingPr, semEnv ); + + } +void inline +handleEndFnSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, + VPThdSemEnv *semEnv ) + { + VPThdSingleton *singleton; + + singleton = &(semEnv->fnSingletons[ semReq->singletonID ]); + handleEndSingleton_helper( singleton, requestingPr, semEnv ); + } +void inline +handleEndDataSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, + VPThdSemEnv *semEnv ) + { + VPThdSingleton *singleton; + + singleton = *(semReq->singletonPtrAddr); + handleEndSingleton_helper( singleton, requestingPr, semEnv ); + } + /*This executes the function in the masterVP, take the function * pointer out of the request and call it, then resume the VP. diff -r e960a8d18f7c -r 505d3c674ce8 VPThread_Request_Handlers.h --- a/VPThread_Request_Handlers.h Tue Nov 16 16:04:29 2010 +0100 +++ b/VPThread_Request_Handlers.h Sat Nov 20 08:39:58 2010 +0100 @@ -14,27 +14,33 @@ /*This header defines everything specific to the VPThread semantic plug-in */ -void +inline void handleMakeMutex( VPThdSemReq *semReq, VPThdSemEnv *semEnv); -void +inline void handleMutexLock( VPThdSemReq *semReq, VPThdSemEnv *semEnv); -void +inline void handleMutexUnlock(VPThdSemReq *semReq, VPThdSemEnv *semEnv); -void +inline void handleMakeCond( VPThdSemReq *semReq, VPThdSemEnv *semEnv); -void +inline void handleCondWait( VPThdSemReq *semReq, VPThdSemEnv *semEnv); -void +inline void handleCondSignal( VPThdSemReq *semReq, VPThdSemEnv *semEnv); void inline handleMalloc(VPThdSemReq *semReq, VirtProcr *requestingPr,VPThdSemEnv *semEnv); void inline handleFree( VPThdSemReq *semReq, VirtProcr *requestingPr, VPThdSemEnv *semEnv); -void inline -handleStartSingleton( VPThdSemReq *semReq, VirtProcr *reqstingPr, +inline void +handleStartFnSingleton( VPThdSemReq *semReq, VirtProcr *reqstingPr, VPThdSemEnv *semEnv ); -void inline -handleEndSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, +inline void +handleEndFnSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, + VPThdSemEnv *semEnv ); +inline void +handleStartDataSingleton( VPThdSemReq *semReq, VirtProcr *reqstingPr, + VPThdSemEnv *semEnv ); +inline void +handleEndDataSingleton( VPThdSemReq *semReq, VirtProcr *requestingPr, VPThdSemEnv *semEnv ); void inline handleAtomic( VPThdSemReq *semReq, VirtProcr *requestingPr, diff -r e960a8d18f7c -r 505d3c674ce8 VPThread_lib.c --- a/VPThread_lib.c Tue Nov 16 16:04:29 2010 +0100 +++ b/VPThread_lib.c Sat Nov 20 08:39:58 2010 +0100 @@ -123,7 +123,7 @@ */ inline void VPThread__start_primitive() - { saveLowTimeStampCountInto( ((VPThreadSemEnv *)(_VMSMasterEnv->semanticEnv))-> + { saveLowTimeStampCountInto( ((VPThdSemEnv *)(_VMSMasterEnv->semanticEnv))-> primitiveStartTime ); } @@ -190,7 +190,7 @@ for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ ) { - readyVPQs[ coreIdx ] = makePrivQ(); + readyVPQs[ coreIdx ] = makeVMSPrivQ(); } semanticEnv->readyVPQs = readyVPQs; @@ -209,10 +209,11 @@ //semanticEnv->transactionStrucs = makeDynArrayInfo( ); for( i = 0; i < NUM_STRUCS_IN_SEM_ENV; i++ ) { - semanticEnv->singletons[i].hasBeenStarted = FALSE; - semanticEnv->singletons[i].hasFinished = FALSE; - semanticEnv->singletons[i].waitQ = makePrivQ; - semanticEnv->transactionStrucs[i].waitingVPQ = makePrivQ(); + semanticEnv->fnSingletons[i].endInstrAddr = NULL; + semanticEnv->fnSingletons[i].hasBeenStarted = FALSE; + semanticEnv->fnSingletons[i].hasFinished = FALSE; + semanticEnv->fnSingletons[i].waitQ = makeVMSPrivQ(); + semanticEnv->transactionStrucs[i].waitingVPQ = makeVMSPrivQ(); } } @@ -438,40 +439,95 @@ VMS__send_sem_request( &reqData, signallingPr ); } -//============================================================================ -/*Uses ID as index into array of flags. If flag already set, resumes from - * end-label. Else, sets flag and resumes normally. + +//=========================================================================== +// +/*A function singleton is a function whose body executes exactly once, on a + * single core, no matter how many times the fuction is called and no + * matter how many cores or the timing of cores calling it. + * + *A data singleton is a ticket attached to data. That ticket can be used + * to get the data through the function exactly once, no matter how many + * times the data is given to the function, and no matter the timing of + * trying to get the data through from different cores. */ -inline void -VPThread__start_singleton( int32 singletonID, VirtProcr *animPr ) + +/*Fn singleton uses ID as index into array of singleton structs held in the + * semantic environment. + */ +void +VPThread__start_fn_singleton( int32 singletonID, VirtProcr *animPr ) { VPThdSemReq reqData; - reqData.reqType = singleton_start; + // + reqData.reqType = singleton_fn_start; reqData.singletonID = singletonID; VMS__send_sem_request( &reqData, animPr ); + if( animPr->dataRetFromReq ) //will be 0 or addr of label in end singleton + { + asm volatile("movl %0, %%eax; \ + jmp *%%eax" \ + /* outputs */ : \ + /* inputs */ : "g"(animPr->dataRetFromReq) \ + /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx","%edi","%esi"\ + ); + } } +/*Data singleton hands addr of loc holding a pointer to a singleton struct. + * The start_data_singleton makes the structure and puts its addr into the + * location. + */ +void +VPThread__start_data_singleton( VPThdSingleton **singletonAddr, VirtProcr *animPr ) + { + VPThdSemReq reqData; + + if( *singletonAddr && (*singletonAddr)->hasFinished ) + goto JmpToEndSingleton; + // + reqData.reqType = singleton_data_start; + reqData.singletonPtrAddr = singletonAddr; + + VMS__send_sem_request( &reqData, animPr ); + if( animPr->dataRetFromReq ) //either 0 or end singleton's return addr + { //Assembly code changes the return addr on the stack to the one + // saved into the singleton by the end-singleton-fn + //The stack's return addr is at 0x4(%%ebp) + JmpToEndSingleton: + asm volatile("movl %0, %%eax; \ + movl (%%eax), %%ebx; \ + movl (%%ebx), %%eax; \ + movl %%eax, 0x4(%%ebp);" \ + /* outputs */ : \ + /* inputs */ : "m"(singletonAddr) \ + /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx","%edi","%esi"\ + ); + } + //now, simply return + //will exit either from the start singleton call or the end-singleton call + } /*Uses ID as index into array of flags. If flag already set, resumes from * end-label. Else, sets flag and resumes normally. * *Note, this call cannot be inlined because the instr addr at the label - * inside is shared by all invocations of singleton. + * inside is shared by all invocations of a given singleton ID. */ void -VPThread__end_singleton( int32 singletonID, VirtProcr *animPr ) +VPThread__end_fn_singleton( int32 singletonID, VirtProcr *animPr ) { VPThdSemReq reqData; //don't need this addr until after at least one singleton has reached // this function VPThdSemEnv *semEnv = VMS__give_sem_env_for( animPr ); - semEnv->singletons[ singletonID ].endInstrAddr = &&EndSingletonInstrAddr; - - reqData.reqType = singleton_end; + semEnv->fnSingletons[ singletonID].endInstrAddr = &&EndSingletonInstrAddr; + + reqData.reqType = singleton_fn_end; reqData.singletonID = singletonID; VMS__send_sem_request( &reqData, animPr ); @@ -480,6 +536,38 @@ return; } +void +VPThread__end_data_singleton( VPThdSingleton **singletonPtrAddr, VirtProcr *animPr ) + { + VPThdSemReq reqData; + + //don't need this addr until after singleton struct has reached + // this function for first time + //do assembly that saves the return addr of this fn call into the + // data singleton -- that data-singleton can only be given to exactly + // one instance in the code of this function. However, can use this + // function in different places for different data-singletons. +// (*(singletonAddr))->endInstrAddr = &&EndDataSingletonInstrAddr; + + //Assembly code takes the return addr off the stack and saves + // into the singleton. The first field in the singleton is the + // "endInstrAddr" field, and the return addr is at 0x4(%%ebp) + asm volatile("movl 0x4(%%ebp), %%eax; \ + movl %0, %%ebx; \ + movl (%%ebx), %%ecx; \ + movl %%eax, (%%ecx);" \ + /* outputs */ : \ + /* inputs */ : "m"(singletonPtrAddr) \ + /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx","%edi","%esi"\ + ); + + reqData.reqType = singleton_data_end; + reqData.singletonPtrAddr = singletonPtrAddr; + + VMS__send_sem_request( &reqData, animPr ); + } + + /*This executes the function in the masterVP, so it executes in isolation * from any other copies -- only one copy of the function can ever execute * at a time.