seanhalle@29: /* seanhalle@29: * Copyright 2010 OpenSourceCodeStewardshipFoundation seanhalle@29: * seanhalle@29: * Licensed under BSD seanhalle@29: */ seanhalle@29: seanhalle@29: #include seanhalle@29: #include seanhalle@29: seanhalle@29: #include "VMS_impl/VMS.h" seanhalle@29: #include "Vthread.h" seanhalle@29: #include "Vthread_helper.h" seanhalle@29: #include "C_Libraries/Queue_impl/PrivateQueue.h" seanhalle@29: #include "C_Libraries/Hash_impl/PrivateHash.h" seanhalle@29: seanhalle@29: seanhalle@29: //========================================================================== seanhalle@29: seanhalle@29: void seanhalle@29: Vthread__init(); seanhalle@29: seanhalle@29: void seanhalle@29: Vthread__init_Seq(); seanhalle@29: seanhalle@29: void seanhalle@29: Vthread__init_Helper(); seanhalle@29: seanhalle@29: seanhalle@29: //=========================================================================== seanhalle@29: seanhalle@29: /*These are the library functions *called in the application* seanhalle@29: * seanhalle@29: */ seanhalle@29: seanhalle@29: seanhalle@29: seanhalle@29: //=========================================================================== seanhalle@29: seanhalle@29: inline int32 seanhalle@29: Vthread__giveMinWorkUnitCycles( float32 percentOverhead ) seanhalle@29: { seanhalle@29: return MIN_WORK_UNIT_CYCLES; seanhalle@29: } seanhalle@29: seanhalle@29: inline int32 seanhalle@29: Vthread__giveIdealNumWorkUnits() seanhalle@29: { seanhalle@29: return NUM_SCHED_SLOTS * NUM_CORES; seanhalle@29: } seanhalle@29: seanhalle@29: inline int32 seanhalle@29: Vthread__give_number_of_cores_to_schedule_onto() seanhalle@29: { seanhalle@29: return NUM_CORES; seanhalle@29: } seanhalle@29: seanhalle@29: /*For now, use TSC -- later, make these two macros with assembly that first seanhalle@29: * saves jump point, and second jumps back several times to get reliable time seanhalle@29: */ seanhalle@29: inline void seanhalle@29: Vthread__start_primitive() seanhalle@29: { saveLowTimeStampCountInto( ((VthdSemEnv *)(_VMSMasterEnv->semanticEnv))-> seanhalle@29: primitiveStartTime ); seanhalle@29: } seanhalle@29: seanhalle@29: /*Just quick and dirty for now -- make reliable later seanhalle@29: * will want this to jump back several times -- to be sure cache is warm seanhalle@29: * because don't want comm time included in calc-time measurement -- and seanhalle@29: * also to throw out any "weird" values due to OS interrupt or TSC rollover seanhalle@29: */ seanhalle@29: inline int32 seanhalle@29: Vthread__end_primitive_and_give_cycles() seanhalle@29: { int32 endTime, startTime; seanhalle@29: //TODO: fix by repeating time-measurement seanhalle@29: saveLowTimeStampCountInto( endTime ); seanhalle@29: startTime=((VthdSemEnv*)(_VMSMasterEnv->semanticEnv))->primitiveStartTime; seanhalle@29: return (endTime - startTime); seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: seanhalle@29: //=========================================================================== seanhalle@29: seanhalle@29: /*Re-use this in the entry-point fn seanhalle@29: */ seanhalle@29: inline SlaveVP * seanhalle@29: Vthread__create_slaveVP_helper( TopLevelFnPtr fnPtr, void *initData, seanhalle@29: VthdSemEnv *semEnv, int32 coreToScheduleOnto ) seanhalle@29: { SlaveVP *newSlv; seanhalle@29: VthdSemData *semData; seanhalle@29: seanhalle@29: //This is running in master, so use internal version seanhalle@29: newSlv = VMS_WL__create_slaveVP( fnPtr, initData ); seanhalle@29: seanhalle@29: semData = VMS_WL__malloc( sizeof(VthdSemData) ); seanhalle@29: semData->highestTransEntered = -1; seanhalle@29: semData->lastTransEntered = NULL; seanhalle@29: seanhalle@29: newSlv->semanticData = semData; seanhalle@29: seanhalle@29: //=================== Assign new processor to a core ===================== seanhalle@29: #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE seanhalle@29: newSlv->coreAnimatedBy = 0; seanhalle@29: seanhalle@29: #else seanhalle@29: seanhalle@29: if(coreToScheduleOnto < 0 || coreToScheduleOnto >= NUM_CORES ) seanhalle@29: { //out-of-range, so round-robin assignment seanhalle@29: newSlv->coreAnimatedBy = semEnv->nextCoreToGetNewSlv; seanhalle@29: seanhalle@29: if( semEnv->nextCoreToGetNewSlv >= NUM_CORES - 1 ) seanhalle@29: semEnv->nextCoreToGetNewSlv = 0; seanhalle@29: else seanhalle@29: semEnv->nextCoreToGetNewSlv += 1; seanhalle@29: } seanhalle@29: else //core num in-range, so use it seanhalle@29: { newSlv->coreAnimatedBy = coreToScheduleOnto; seanhalle@29: } seanhalle@29: #endif seanhalle@29: //======================================================================== seanhalle@29: seanhalle@29: return newSlv; seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: /* seanhalle@29: */ seanhalle@29: inline SlaveVP * seanhalle@29: Vthread__create_thread( TopLevelFnPtr fnPtr, void *initData, seanhalle@29: SlaveVP *creatingSlv ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: //the semantic request data is on the stack and disappears when this seanhalle@29: // call returns -- it's guaranteed to remain in the Slv's stack for as seanhalle@29: // long as the Slv is suspended. seanhalle@29: reqData.reqType = 0; //know the type because is a VMS create req seanhalle@29: reqData.coreToScheduleOnto = -1; //means round-robin schedule seanhalle@29: reqData.fnPtr = fnPtr; seanhalle@29: reqData.initData = initData; seanhalle@29: reqData.requestingSlv = creatingSlv; seanhalle@29: seanhalle@29: VMS_WL__send_create_slaveVP_req( &reqData, creatingSlv ); seanhalle@29: seanhalle@29: return creatingSlv->dataRetFromReq; seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: inline SlaveVP * seanhalle@29: Vthread__create_thread_with_affinity( TopLevelFnPtr fnPtr, void *initData, seanhalle@29: SlaveVP *creatingSlv, int32 coreToScheduleOnto ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: //the semantic request data is on the stack and disappears when this seanhalle@29: // call returns -- it's guaranteed to remain in the Slv's stack for as seanhalle@29: // long as the Slv is suspended. seanhalle@29: reqData.reqType = 0; //know type because in a VMS create req seanhalle@29: reqData.coreToScheduleOnto = coreToScheduleOnto; seanhalle@29: reqData.fnPtr = fnPtr; seanhalle@29: reqData.initData = initData; seanhalle@29: reqData.requestingSlv = creatingSlv; seanhalle@29: seanhalle@29: VMS_WL__send_create_slaveVP_req( &reqData, creatingSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: inline void seanhalle@29: Vthread__dissipate_thread( SlaveVP *procrToDissipate ) seanhalle@29: { seanhalle@29: VMS_WL__send_dissipate_req( procrToDissipate ); seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: //=========================================================================== seanhalle@29: seanhalle@29: void * seanhalle@29: Vthread__malloc( size_t sizeToMalloc, SlaveVP *animSlv ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = malloc_req; seanhalle@29: reqData.sizeToMalloc = sizeToMalloc; seanhalle@29: reqData.requestingSlv = animSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: seanhalle@29: return animSlv->dataRetFromReq; seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: /*Sends request to Master, which does the work of freeing seanhalle@29: */ seanhalle@29: void seanhalle@29: Vthread__free( void *ptrToFree, SlaveVP *animSlv ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = free_req; seanhalle@29: reqData.ptrToFree = ptrToFree; seanhalle@29: reqData.requestingSlv = animSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: //=========================================================================== seanhalle@29: seanhalle@29: inline void seanhalle@29: Vthread__set_globals_to( void *globals ) seanhalle@29: { seanhalle@29: ((VthdSemEnv *) seanhalle@29: (_VMSMasterEnv->semanticEnv))->applicationGlobals = globals; seanhalle@29: } seanhalle@29: seanhalle@29: inline void * seanhalle@29: Vthread__give_globals() seanhalle@29: { seanhalle@29: return((VthdSemEnv *) (_VMSMasterEnv->semanticEnv))->applicationGlobals; seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: //=========================================================================== seanhalle@29: seanhalle@29: inline int32 seanhalle@29: Vthread__make_mutex( SlaveVP *animSlv ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = make_mutex; seanhalle@29: reqData.requestingSlv = animSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: seanhalle@29: return (int32)animSlv->dataRetFromReq; //mutexid is 32bit wide seanhalle@29: } seanhalle@29: seanhalle@29: inline void seanhalle@29: Vthread__mutex_lock( int32 mutexIdx, SlaveVP *acquiringSlv ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = mutex_lock; seanhalle@29: reqData.mutexIdx = mutexIdx; seanhalle@29: reqData.requestingSlv = acquiringSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, acquiringSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: inline void seanhalle@29: Vthread__mutex_unlock( int32 mutexIdx, SlaveVP *releasingSlv ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = mutex_unlock; seanhalle@29: reqData.mutexIdx = mutexIdx; seanhalle@29: reqData.requestingSlv = releasingSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, releasingSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: //======================= seanhalle@29: inline int32 seanhalle@29: Vthread__make_cond( int32 ownedMutexIdx, SlaveVP *animSlv) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = make_cond; seanhalle@29: reqData.mutexIdx = ownedMutexIdx; seanhalle@29: reqData.requestingSlv = animSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: seanhalle@29: return (int32)animSlv->dataRetFromReq; //condIdx is 32 bit wide seanhalle@29: } seanhalle@29: seanhalle@29: inline void seanhalle@29: Vthread__cond_wait( int32 condIdx, SlaveVP *waitingSlv) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = cond_wait; seanhalle@29: reqData.condIdx = condIdx; seanhalle@29: reqData.requestingSlv = waitingSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, waitingSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: inline void * seanhalle@29: Vthread__cond_signal( int32 condIdx, SlaveVP *signallingSlv ) seanhalle@29: { VthdSemReq reqData; seanhalle@29: seanhalle@29: reqData.reqType = cond_signal; seanhalle@29: reqData.condIdx = condIdx; seanhalle@29: reqData.requestingSlv = signallingSlv; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, signallingSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: //=========================================================================== seanhalle@29: // seanhalle@29: /*A function singleton is a function whose body executes exactly once, on a seanhalle@29: * single core, no matter how many times the fuction is called and no seanhalle@29: * matter how many cores or the timing of cores calling it. seanhalle@29: * seanhalle@29: *A data singleton is a ticket attached to data. That ticket can be used seanhalle@29: * to get the data through the function exactly once, no matter how many seanhalle@29: * times the data is given to the function, and no matter the timing of seanhalle@29: * trying to get the data through from different cores. seanhalle@29: */ seanhalle@29: seanhalle@29: /*Fn singleton uses ID as index into array of singleton structs held in the seanhalle@29: * semantic environment. seanhalle@29: */ seanhalle@29: void seanhalle@29: Vthread__start_fn_singleton( int32 singletonID, SlaveVP *animSlv ) seanhalle@29: { seanhalle@29: VthdSemReq reqData; seanhalle@29: seanhalle@29: // seanhalle@29: reqData.reqType = singleton_fn_start; seanhalle@29: reqData.singletonID = singletonID; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: if( animSlv->dataRetFromReq != 0 ) //addr of matching end-singleton seanhalle@29: { seanhalle@29: VthdSemEnv *semEnv = VMS_int__give_sem_env_for( animSlv ); //not protected! seanhalle@29: VMS_int__return_to_addr_in_ptd_to_loc( seanhalle@29: &((semEnv->fnSingletons[singletonID]).savedRetAddr) ); seanhalle@29: } seanhalle@29: } seanhalle@29: seanhalle@29: /*Data singleton hands addr of loc holding a pointer to a singleton struct. seanhalle@29: * The start_data_singleton makes the structure and puts its addr into the seanhalle@29: * location. seanhalle@29: */ seanhalle@29: void seanhalle@29: Vthread__start_data_singleton( VthdSingleton *singleton, SlaveVP *animSlv ) seanhalle@29: { seanhalle@29: VthdSemReq reqData; seanhalle@29: seanhalle@29: if( singleton->savedRetAddr && singleton->hasFinished ) seanhalle@29: goto JmpToEndSingleton; seanhalle@29: seanhalle@29: reqData.reqType = singleton_data_start; seanhalle@29: reqData.singleton = singleton; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: if( animSlv->dataRetFromReq ) //either 0 or end singleton's return addr seanhalle@29: { seanhalle@29: JmpToEndSingleton: seanhalle@29: VMS_int__return_to_addr_in_ptd_to_loc(&(singleton->savedRetAddr)); seanhalle@29: } seanhalle@29: //now, simply return seanhalle@29: //will exit either from the start singleton call or the end-singleton call seanhalle@29: } seanhalle@29: seanhalle@29: /*Uses ID as index into array of flags. If flag already set, resumes from seanhalle@29: * end-label. Else, sets flag and resumes normally. seanhalle@29: * seanhalle@29: *Note, this call cannot be inlined because the instr addr at the label seanhalle@29: * inside is shared by all invocations of a given singleton ID. seanhalle@29: */ seanhalle@29: void seanhalle@29: Vthread__end_fn_singleton( int32 singletonID, SlaveVP *animSlv ) seanhalle@29: { seanhalle@29: VthdSemReq reqData; seanhalle@29: seanhalle@29: //don't need this addr until after at least one singleton has reached seanhalle@29: // this function seanhalle@29: VthdSemEnv *semEnv = VMS_int__give_sem_env_for( animSlv ); seanhalle@29: VMS_int__return_to_addr_in_ptd_to_loc( seanhalle@29: &((semEnv->fnSingletons[singletonID]).savedRetAddr) ); seanhalle@29: seanhalle@29: reqData.reqType = singleton_fn_end; seanhalle@29: reqData.singletonID = singletonID; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: void seanhalle@29: Vthread__end_data_singleton( VthdSingleton *singleton, SlaveVP *animSlv ) seanhalle@29: { seanhalle@29: VthdSemReq reqData; seanhalle@29: seanhalle@29: //don't need this addr until after singleton struct has reached seanhalle@29: // this function for first time seanhalle@29: //do assembly that saves the return addr of this fn call into the seanhalle@29: // data singleton -- that data-singleton can only be given to exactly seanhalle@29: // one instance in the code of this function. However, can use this seanhalle@29: // function in different places for different data-singletons. seanhalle@29: seanhalle@29: VMS_int__save_return_into_ptd_to_loc_then_do_ret(&(singleton->savedRetAddr)); seanhalle@29: seanhalle@29: reqData.reqType = singleton_data_end; seanhalle@29: reqData.singleton = singleton; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: /*This executes the function in the masterVP, so it executes in isolation seanhalle@29: * from any other copies -- only one copy of the function can ever execute seanhalle@29: * at a time. seanhalle@29: * seanhalle@29: *It suspends to the master, and the request handler takes the function seanhalle@29: * pointer out of the request and calls it, then resumes the Slv. seanhalle@29: *Only very short functions should be called this way -- for longer-running seanhalle@29: * isolation, use transaction-start and transaction-end, which run the code seanhalle@29: * between as work-code. seanhalle@29: */ seanhalle@29: void seanhalle@29: Vthread__animate_short_fn_in_isolation( PtrToAtomicFn ptrToFnToExecInMaster, seanhalle@29: void *data, SlaveVP *animSlv ) seanhalle@29: { seanhalle@29: VthdSemReq reqData; seanhalle@29: seanhalle@29: // seanhalle@29: reqData.reqType = atomic; seanhalle@29: reqData.fnToExecInMaster = ptrToFnToExecInMaster; seanhalle@29: reqData.dataForFn = data; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: seanhalle@29: /*This suspends to the master. seanhalle@29: *First, it looks at the Slv's data, to see the highest transactionID that Slv seanhalle@29: * already has entered. If the current ID is not larger, it throws an seanhalle@29: * exception stating a bug in the code. Otherwise it puts the current ID seanhalle@29: * there, and adds the ID to a linked list of IDs entered -- the list is seanhalle@29: * used to check that exits are properly ordered. seanhalle@29: *Next it is uses transactionID as index into an array of transaction seanhalle@29: * structures. seanhalle@29: *If the "Slv_currently_executing" field is non-null, then put requesting Slv seanhalle@29: * into queue in the struct. (At some point a holder will request seanhalle@29: * end-transaction, which will take this Slv from the queue and resume it.) seanhalle@29: *If NULL, then write requesting into the field and resume. seanhalle@29: */ seanhalle@29: void seanhalle@29: Vthread__start_transaction( int32 transactionID, SlaveVP *animSlv ) seanhalle@29: { seanhalle@29: VthdSemReq reqData; seanhalle@29: seanhalle@29: // seanhalle@29: reqData.reqType = trans_start; seanhalle@29: reqData.transID = transactionID; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: } seanhalle@29: seanhalle@29: /*This suspends to the master, then uses transactionID as index into an seanhalle@29: * array of transaction structures. seanhalle@29: *It looks at Slv_currently_executing to be sure it's same as requesting Slv. seanhalle@29: * If different, throws an exception, stating there's a bug in the code. seanhalle@29: *Next it looks at the queue in the structure. seanhalle@29: *If it's empty, it sets Slv_currently_executing field to NULL and resumes. seanhalle@29: *If something in, gets it, sets Slv_currently_executing to that Slv, then seanhalle@29: * resumes both. seanhalle@29: */ seanhalle@29: void seanhalle@29: Vthread__end_transaction( int32 transactionID, SlaveVP *animSlv ) seanhalle@29: { seanhalle@29: VthdSemReq reqData; seanhalle@29: seanhalle@29: // seanhalle@29: reqData.reqType = trans_end; seanhalle@29: reqData.transID = transactionID; seanhalle@29: seanhalle@29: VMS_WL__send_sem_request( &reqData, animSlv ); seanhalle@29: } seanhalle@29: //===========================================================================