# HG changeset patch # User Me # Date 1274581991 25200 # Node ID 35b53e6de714a67f24557e884caa921249c1992e Initial add -- up on sourceforge now diff -r 000000000000 -r 35b53e6de714 VMSHW.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VMSHW.h Sat May 22 19:33:11 2010 -0700 @@ -0,0 +1,46 @@ +/* + * Copyright 2009 OpenSourceStewardshipFoundation.org + * Licensed under GNU General Public License version 2 + * + * Author: seanhalle@yahoo.com + * + */ + +#ifndef _VMSHW_H +#define _VMSHW_H + +#include "VMS/VMS_primitive_data_types.h" +#include "VMS/Queue_impl/BlockingQueue.h" + +/*This header defines everything specific to the VMSHW semantic plug-in + */ +typedef struct VMSHWReqData VMSHWReqData; + +typedef struct + { + } +VMSHWProcr; + +/*Semantic layer-specific data sent inside a request from lib called in app + * to request handler called in MasterLoop + */ +enum VMSHW_ReqType + { + receive, + send, + create + }; + +struct VMSHWReqData + { VMSHW_ReqType reqType; + + }; + +typedef struct + { + + } +VMSHWSemanticEnv; + +#endif /* _VMSHW_H */ + diff -r 000000000000 -r 35b53e6de714 VMSHW_PluginFns.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VMSHW_PluginFns.c Sat May 22 19:33:11 2010 -0700 @@ -0,0 +1,141 @@ +/* + * Copyright 2010 OpenSourceCodeStewardshipFoundation + * + * Licensed under BSD + */ + +#include +#include +#include + +#include "VMS/VMS.h" +#include "VMS/Queue_impl/PrivateQueue.h" +#include "VMSHW.h" + + +/*Will get requests to send, to receive, and to create new processors. + * Upon send, check the hash to see if a receive is waiting. + * Upon receive, check hash to see if a send has already happened. + * When other is not there, put in. When other is there, the comm. + * completes, which means the receiver P gets a new work-unit generated + * that picks up right after the receive request. So make the work-unit + * and put it into the queue of work-units ready to go. + * Other request is create a new Processor, with the function to run in the + * Processor, and initial data. + */ + +/*Old idea, but want to keep around: + * give pointer to initialization function to create-processor call, in + * addition to the work-function and initial data + * Then, make the initialization be a work-unit, that places the created + * processor-struc, and an initial work-unit for that processor in a + * request that comes to the req handler when done. The initial work-unit + * has the pointer to a special VMS code snippet that + * the function to run in the Processor is pointed to by the work-unit + * code pointer, and the initial data as the work-unit's data-pointer. + * Another request is the result of create-processor request. It has a new + * Processor, and the processor's inital work-unit. Add the Processor to + * the data-structures that track Processors, and put the work-unit into the + * queue of ready-to-go work-units. + * The reason to make the create-processor work-unit return the new + * processor and its initial work unit, is that only the MasterLoop + * work-unit owns the semantic environment, with the Processor-tracking + * data-structures, and only it can put work-units into or take out of + * the ready-to-go work-unit queue. + */ +void +VMSHW__Request_Handler( VMSProcr *slave, void *semanticEnv ) + { VMSHWEnviron semEnv; + + semEnv = (VMSHWEnviron *)semanticEnv; + + req = slave->requestsToMaster; + while( req != NULL ) + { + //TODO: figure out better separation -- maybe MasterLoop gives + // request payloads one at a time to this handler + if( req->type == VMSInternal ) /*do something*/; + else //request is to be handled by VMSHW + { + semanticReq = req->reqPayload; //TODO: make sure not NULL in creator + switch( semanticReq->type ) + { + case sendFromName: handleSendFromName( semanticReq, semEnv); + break; + case sendToPort: handleSendToPort( semanticReq, semEnv); + break; + case receiveFromName: handleReceiveFromName(semanticReq, semEnv); + break; + case receiveOnPort: handleReceiveOnPort( semanticReq, semEnv); + break; + case create: handleCreate( semanticReq, semEnv); + break; + } + } + req = req->next; + } + } + +/*The payload has the receiving processor and its port + * + *Note one value in this approach: without the extra (VMS) virtual layer, + * the send and receive would happen in real time instead of virtual time, + * which would waste real time while one of them waited for other + */ + void +handleSendToPort( VMSHWRequest *reqPayload, VMSHWEnviron *semEnv ) + { + reqProcr = reqPayload->reqProcr; + receivePr = reqPayload->receivePr; + + key = receivePr->keys[ reqPayload->portNum ]; + value = getValueFromTable( key, semEnv->commHash ); + if( value == NULL ) + { + //TODO: is hash entry really just a flag -- empty? Want work-unit? No + // 'cause want to make an entry when only a send, no receive yet + value = malloc( sizeof(CommHashFlag) ); + putValueIntoTable( key, value, semEnv->commHash ); + } + else //receive waiting for this send + { + workUPayload = malloc( sizeof(VMSHWWorkUnit) ); + workUPayload->owningProcr = receivePr; + newWorkUnit = VMS__create_workUnit( workUPayload ); + + writePrivQ( newWorkUnit, semEnv->readyWorkUnitQ ); + + //NOTE: this is sequential! Don't have to worry about sharing or syncs + //in semantic environment -- even though shared by work units, slaves, + // and virtual processors -- all the sharing is in virtual time, and + // mapped onto safe sequence in physical time by VMS + + } + } + + +/*For VMSHW, scheduling a slave simply takes the next work-unit off the + * ready-to-go work-unit queue and assigns it to the slaveToSched. + *If the ready-to-go work-unit queue is empty, then nothing to schedule + * to the slave -- return FALSE to let Master loop know scheduling that + * slave failed. + */ +bool8 +VMSHW__schedule_slave( VMSProcr *slaveToSched, void *semanticEnv ) + { PrivQueueStruc *readyWorkUnitQ; + WorkUnit *workUnitToSched; + VMSHWSemanticEnv *semEnv; + + semEnv = (VMSHWSemanticEnv *)semanticEnv; + + //Note, using a non-blocking queue -- it returns NULL if queue empty + readyWorkUnitQ = semEnv->readyWorkUnitQ; + + workUnitToSched = readPrivQ( readyWorkUnitQ ); + if( readQ == NULL ) return FALSE; + + slaveToSched->workUnitToDo = workUnitToSched; + workUnitToSched->slaveAssignedTo = slaveToSched; + + return TRUE; + } diff -r 000000000000 -r 35b53e6de714 VMSHW_lib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VMSHW_lib.c Sat May 22 19:33:11 2010 -0700 @@ -0,0 +1,230 @@ +/* + * Copyright 2010 OpenSourceCodeStewardshipFoundation + * + * Licensed under BSD + */ + +#include +#include +#include + +#include "../VMS/VMS.h" +#include "VMSHW.h" +#include "../VMS/Queue_impl/NonBlockingQueue.h" + + +/*These are the library functions *called in the application* + *They all run in the virtual slaves, never in the virtual master. + * + *There's a pattern for the outside sequential code to interact with the + * VMSHW code. + *The VMSHW system is inside a boundary.. every VMSHW system is in its + * own directory that contains the functions for each of the processor types. + * One of the processor types is the "seed" processor that starts the + * cascade of creating all the processors that do the work. + *So, in the directory is a file called "EntryPoint.c" that contains the + * function, named appropriately to the work performed, that the outside + * sequential code calls. This function follows a pattern: + *1) it calls VMSHW__init() + *2) it creates the initial data for the seed processor, which is passed + * in to the function + *3) it creates the seed VMSHW processor, with the data to start it with. + *4) it calls startVMSHWThenWaitUntilWorkDone + *5) it gets the returnValue from the transfer struc and returns that + * from the function + * + *For now, a new VMSHW system has to be created via VMSHW__init every + * time an entry point function is called -- later, might add letting the + * VMSHW system be created once, and let all the entry points just reuse + * it -- want to be as simple as possible now, and see by using what makes + * sense for later.. + */ + + +/*Initializes all the data-structures for a VMSHW system -- but doesn't + * start it running yet! + * + * + *This sets up the semantic layer over the VMS system + * + *First, calls VMS_Setup, then creates own environment, making it ready + * for creating the seed processor. + */ + void +VMSHW__init() + { VMSHWEnv *semanticEnv; + + init_VMS(); + + //masterEnv, a global var, now is partially set up by init_VMS + semanticEnv = malloc( sizeof( VMSHWEnv ) ); + masterEnv->semanticEnv = semanticEnv; + + semanticEnv->readyWorkUnitsQ = mareadyWorkUnitsQkePrivQ(); + } + + +/*This is the entry point to the VMSHW system from the sequential part of + * the app.. + * + *Calling this starts the VMS system, passing it the input data given to + * this, then waits for the work to finish, and returns whatever pointer + * the final VMSHW call said to return to the outside via the + * VMSHW__transfer_out_as_result call + */ + +VMSHW__start_then_wait_until_work_done() + { + VMS__start(); + + //Wait for work to complete + //Use PThreads to put the main thread to sleep until something executes + // a notify(), which is the last thing VMS does when work is done + + + //This will suspend the main PThread until all VMSHW processors have + // performed dissipate_self(), at which point the VMSHW system will + // shut itself down, then shut VMS down then call notify to make this + // wait wake up the main thread again. + status = + pthread_cond_wait( &VMS_Environ->doneCondition/*,&VMS_Environ->doneLock*/); + if (status != 0){perror("Error waiting for work to finish\n"); exit(1);} + + //signal like this: pthread_cond_signal( &VMS_Environ->doneCondition ); + + } + +/*TODO: Q: dealing with library f()s and DKU vs WT vs FoR + * (still want to do FoR, with time-lines as syntax, could be super cool) + * A: thinking pin the coreLoops for all of BLIS -- let Master arbitrate + * among library, DKU, WT, FoR -- all the patterns in terms of work-units, + * so Master picks which work-units from which portions of app onto which + * Slaves + *Might even do one master plus several slaves for each core -- allows + * single-reader-single-writer sync everywhere -- no atomic primitives + * Might have the different schedulers talk to each other, to negotiate + * larger-grain sharing of resources, according to predicted critical + * path, and expansion of work + */ + + + +/*Causes the VMSHW system to remove internal ownership, so data won't be + * freed when VMSHW shuts down, and will persist in the external program. + */ +void +VMSHW__transfer_to_external( void *data ) + { + + } + +void +VMSHW__create_seed_processor_then_wait_until_work_done + ( VMSHWProcrFnPtr fn, void *data ) + { VMSHWEnviron semEnv; + + VMSHW__init(); + semEnv = masterEnv->semanticEnv; + + //VMSHW starts with one processor, which is put into initial environ, + // and, which then calls create() to create more thereby expanding work + firstVirtProcr = VMSHW__create_processor( ); + + firstWorkUnit = + + writePrivQ( firstWorkUnit, semEnv->readyWorkUnitsQ ); + + //start the VMSHW system then wait for work to end + //NOTE: no Threads should exist in the outside program that might touch + // any of the data reachable from the params given to the seed procr + VMSHW__start_then_wait_until_work_done(); + } + +void +VMSHW__create_processor( ) + { + firstWorkUnit = firstVirtProcr->latestWorkUnit; + + writePrivQ( firstWorkUnit, semanticEnv->readyWorkUnitsQ ); + + return firstVirtProcr; + } + +VMSHWProcrFnPtr + VMSHWProcr * VMSHW__create_processor( &calcVector, vectParams ); + void * VMSHW__malloc( sizeof( VectorParams ) ); + VMSHW__transfer_to_outside( resultMatrix ); + VMSHW__dissipate_self(); //all processors have to dissipate self at end + VMSHW__send( result, resultPr ); + resultsParams->dividerPr = VMSHW__self(); + VMSHW__start_then_wait_until_work_done(); + VMSHW__create_seed_processor( ÷IntoVectors, dividerParams ); +VMSHWProcr + + void * VMSHW__receive_from( self, resultPr ); + void * VMSHW__receive_from_any_but( self, dividerPr ); + void * VMSHW__receive_on_port( self, VECTOR_PORT ); + + VMSHW__transfer_ownership_from_to( resultMatrix, self, dividerPr ); + +void * +VMSHW__receive_from( VMSHWProcr *callingPr, VMSHWProcr *sendingPr ) + { VMSRequest *req; + VMSHWReq reqPayload; + + //hash on the caller, 'cause always know it, but sometimes want to + // receive from anonymous sender + //Q: what happens if no "receive" is outstanding for one sender, but + // do have an outstanding for a different sender? + //need to treat each "from" as a separate port in the hash table + + reqPayload = malloc( sizeof(VMSHWReq) ); + reqPayload->type = receive_from; + reqPayload->key = callingPr->ID ^ sendingPr->ID << 16; //65K max procrs + reqPayload->procr = callingPr; + + req = VMS__create_request( reqPayload ); + VMS__add_request_to_slave( req, callingPr ); + VMS__save_ret_and_jump_to_CoreLoop( callingPr ); + } + + + +//=========================================================================== + void +freeParamStruc( ParamStruc * param ) + { if( param->type == STRING_PARAM_TYPE ) free( param->strValue ); + free( param ); + } + + + ParamStruc * +makeParamStruc() + { ParamStruc *retStruc; + retStruc = malloc( sizeof( ParamStruc ) ); + retStruc->floatValue = 0.0; + retStruc->intValue = 0; + retStruc->strValue = NULL; + } + + ParamStruc * +makeParamFromStrs( char * type, char *value ) + { ParamStruc *retParam; + retParam = makeParamStruc(); + switch(*type) + { case 'i': + { retParam->type = INT_PARAM_TYPE; + retParam->intValue = atoi( value ); + } break; + case 's': + { retParam->type = STRING_PARAM_TYPE; + retParam->strValue = malloc( strlen(value) + 1); + strcpy( retParam->strValue, value ); + } break; + case 'f': + { retParam->type = FLOAT_PARAM_TYPE; + retParam->floatValue = atof( value ); + } break; + } + return retParam; + }