# HG changeset patch # User Me # Date 1278533729 25200 # Node ID 06ca89bafbb8cd5e7b387808b6fdedc4c6a7766d # Parent 35b53e6de714a67f24557e884caa921249c1992e 1st working version -- as far as can tell due to SEH bugs diff -r 35b53e6de714 -r 06ca89bafbb8 VMSHW.h --- a/VMSHW.h Sat May 22 19:33:11 2010 -0700 +++ b/VMSHW.h Wed Jul 07 13:15:29 2010 -0700 @@ -9,38 +9,117 @@ #ifndef _VMSHW_H #define _VMSHW_H -#include "VMS/VMS_primitive_data_types.h" -#include "VMS/Queue_impl/BlockingQueue.h" +#include "VMS/Queue_impl/PrivateQueue.h" +#include "VMS/Hash_impl/PrivateHash.h" +#include "VMS/VMS.h" /*This header defines everything specific to the VMSHW semantic plug-in */ -typedef struct VMSHWReqData VMSHWReqData; +typedef struct _VMSHWSemReq VMSHWSemReq; -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 +enum VMSHWReqType { - receive, - send, - create + send_type = 1, + send_from_to, + receive_any, //order and grouping matter -- send before receive + receive_type, // and receive_any first of the receives -- Handlers + receive_from_to,// rely upon this ordering of enum + transfer_to, + transfer_out }; -struct VMSHWReqData - { VMSHW_ReqType reqType; - - }; +struct _VMSHWSemReq + { enum VMSHWReqType reqType; + VirtProcr *sendPr; + VirtProcr *receivePr; + int32 msgType; + void *msg; + VMSHWSemReq *nextReqInHashEntry; + } +/* VMSHWSemReq */; typedef struct { + PrivQueueStruc *readyVirtProcrQ; + HashTable *commHashTbl; + int numVirtPr; + } +VMSHWSemEnv; - } -VMSHWSemanticEnv; + +//=========================================================================== + +void +VMSHW__create_seed_procr_and_do_work( VirtProcrFnPtr fn, void *initData ); + +//======================= + +void +VMSHW__init(); + +void +VMSHW__shutdown(); + +//======================= + +inline VirtProcr * +VMSHW__create_procr_with( VirtProcrFnPtr fnPtr, void *initData, + VirtProcr *creatingPr ); + +void +VMSHW__dissipate_procr( VirtProcr *procrToDissipate ); + +//======================= +void * +VMSHW__malloc_size_to( int numBytes, VirtProcr *ownerPr ); + +void +VMSHW__transfer_ownership_of_from_to( void *data, VirtProcr *oldOwnerPr, + VirtProcr *newOwnerPr ); + +void +VMSHW__add_ownership_by_to( VirtProcr *newOwnerPr, void *data ); + +void +VMSHW__remove_ownership_by_from( VirtProcr *loserPr, void *dataLosing ); + +void +VMSHW__transfer_ownership_to_outside( void *dataToTransferOwnershipOf ); + + + +//======================= +void +VMSHW__send_of_type_to( VirtProcr *sendPr, void *msg, const int type, + VirtProcr *receivePr); + +void +VMSHW__send_from_to( void *msg, VirtProcr *sendPr, VirtProcr *receivePr); + +void * +VMSHW__receive_type_to( const int type, VirtProcr *receivePr ); + +void * +VMSHW__receive_from_to( VirtProcr *sendPr, VirtProcr *receivePr ); + + +//======================= + +void +VMSHW__free_semantic_request( VMSHWSemReq *semReq ); + + +//========================= Internal use only ============================= +void +VMSHW__Request_Handler( VirtProcr *requestingPr, void *_semEnv ); + +VirtProcr * +VMSHW__schedule_virt_procr( void *_semEnv ); + #endif /* _VMSHW_H */ diff -r 35b53e6de714 -r 06ca89bafbb8 VMSHW_PluginFns.c --- a/VMSHW_PluginFns.c Sat May 22 19:33:11 2010 -0700 +++ b/VMSHW_PluginFns.c Wed Jul 07 13:15:29 2010 -0700 @@ -11,107 +11,84 @@ #include "VMS/VMS.h" #include "VMS/Queue_impl/PrivateQueue.h" #include "VMSHW.h" +#include "VMSHW_Request_Handlers.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 + * completes, which means the receiver P gets scheduled and + * 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. */ +void +VMSHW__Request_Handler( VirtProcr *requestingPr, void *_semEnv ) + { VMSHWSemEnv *semEnv; + VMSReqst *req; + VMSHWSemReq *semReq; + + semEnv = (VMSHWSemEnv *)_semEnv; -/*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; + req = VMS__take_top_request_from( requestingPr ); + 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 + if( VMS__isSemanticReqst( req ) ) { - semanticReq = req->reqPayload; //TODO: make sure not NULL in creator - switch( semanticReq->type ) + semReq = VMS__take_sem_reqst_from( req ); + if( semReq == NULL ) goto DoneWithReqst; + switch( semReq->reqType ) { - case sendFromName: handleSendFromName( semanticReq, semEnv); + case send_type: handleSendType( semReq, semEnv); break; - case sendToPort: handleSendToPort( semanticReq, semEnv); + case send_from_to: handleSendFromTo( semReq, semEnv); break; - case receiveFromName: handleReceiveFromName(semanticReq, semEnv); + case receive_type: handleReceiveType( semReq, semEnv); break; - case receiveOnPort: handleReceiveOnPort( semanticReq, semEnv); + case receive_from_to: handleReceiveFromTo(semReq, semEnv); break; - case create: handleCreate( semanticReq, semEnv); + case transfer_to: handleTransferTo( semReq, semEnv); + VMSHW__free_semantic_request( semReq ); + break; + case transfer_out: handleTransferOut( semReq, semEnv); + VMSHW__free_semantic_request( semReq ); break; } } - req = req->next; - } + else if( VMS__isCreateReqst( req ) ) //only plugin can add to ready Q + { VirtProcr * + newPr = (VirtProcr *)req->semReqData; + semEnv->numVirtPr += 1; + + //resume procr asked for registration & start new pr + writePrivQ( requestingPr, semEnv->readyVirtProcrQ ); + writePrivQ( newPr, semEnv->readyVirtProcrQ ); + } + else if( VMS__isDissipateReqst( req ) ) + { + //free any semantic data allocated to the virt procr + + //Now, call VMS's dissipate fn, which will free stack and rest + VMS__dissipate_procr( requestingPr ); + + semEnv->numVirtPr -= 1; + if( semEnv->numVirtPr == 0 ) + { //no more work, so shutdown -- create shutdown procr & Q it + VirtProcr * shutdownPr = VMS__create_the_shutdown_procr(); + writePrivQ( shutdownPr, semEnv->readyVirtProcrQ ); + } + } + + DoneWithReqst: + VMS__free_request( req ); + req = VMS__take_top_request_from( requestingPr ); + } //while( req != NULL ) } -/*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 @@ -120,22 +97,16 @@ * 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; +VirtProcr * +VMSHW__schedule_virt_procr( void *_semEnv ) + { VirtProcr *schedPr; + VMSHWSemEnv *semEnv; - semEnv = (VMSHWSemanticEnv *)semanticEnv; + semEnv = (VMSHWSemEnv *)_semEnv; + schedPr = readPrivQ( semEnv->readyVirtProcrQ ); //Note, using a non-blocking queue -- it returns NULL if queue empty - readyWorkUnitQ = semEnv->readyWorkUnitQ; - workUnitToSched = readPrivQ( readyWorkUnitQ ); - if( readQ == NULL ) return FALSE; + return( schedPr ); + } - slaveToSched->workUnitToDo = workUnitToSched; - workUnitToSched->slaveAssignedTo = slaveToSched; - - return TRUE; - } diff -r 35b53e6de714 -r 06ca89bafbb8 VMSHW_Request_Handlers.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VMSHW_Request_Handlers.c Wed Jul 07 13:15:29 2010 -0700 @@ -0,0 +1,321 @@ +/* + * Copyright 2010 OpenSourceCodeStewardshipFoundation + * + * Licensed under BSD + */ + +#include +#include +#include + +#include "VMS/VMS.h" +#include "VMS/Queue_impl/PrivateQueue.h" +#include "VMS/Hash_impl/PrivateHash.h" +#include "VMSHW.h" + + + +//=========================================================================== +// Helpers + +HashEntry * +lookupReqAndGiveEntryElseInsertIfEmpty( char *key, VMSHWSemReq *semReq, + HashTable *commHashTbl ) + { HashEntry *entry; + VMSHWSemReq *waitingReq; + + entry = getEntryFromTable( (char *)key, commHashTbl ); + if( entry == NULL ) + { //no waiting sends or receives, so add this request and exit + addValueIntoTable( key, semReq, commHashTbl ); + return NULL; + } + waitingReq = (VMSHWSemReq *)entry->content; + if( waitingReq == NULL ) //might happen when last waiting gets paired + { //no waiting sends or receives, so add this request and exit + entry->content = semReq; + return NULL; + } + return entry; + } + + + + +//=========================================================================== +/*The semantic request has the receiving processor and the message type + * + *Note one value in this approach: without the extra VMS 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 + * + *When successfully pair-up, transfer ownership of the sent data + * to the receiving processor + * + *Messages of a given Type have to be kept separate.. so need a separate + * entry in the hash table for each pair: receivePr, msgType + * + *Also, if same sender sends multiple before any get received, then need to + * stack the sends up -- even if a send waits until it's paired, several + * separate processors can send to the same receiver, and hashing on the + * receive processor, so they will stack up. + */ +void +handleSendType( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv ) + { VirtProcr *sendPr, *receivePr; + int key[] = {0,0,0}; + VMSHWSemReq *waitingReq; + HashEntry *entry; + HashTable *commHashTbl = semEnv->commHashTbl; + + receivePr = semReq->receivePr; //For "send", know both send & recv procrs + sendPr = semReq->sendPr; + + //TODO: handle transfer of msg-locs ownership + //TODO: hash table implemented such that using "addEntry" or + // "addValue" to table causes the *value* in old entry to be + // *freed* -- this is bad. Want to stack up values in a linked + // list when multiple have the same key. + + //TODO: use a faster hash function -- see notes in intelligence gather + key[0] = (int)receivePr; + key[1] = (int)(semReq->msgType); + //key[2] acts at the 0 that terminates the string + + entry = lookupReqAndGiveEntryElseInsertIfEmpty( key, semReq, commHashTbl); + if( entry == NULL ) return; //was just inserted + + waitingReq = (VMSHWSemReq *)entry->content; + + //At this point, know have waiting request(s) -- either sends or recv + //Note, can only have max of one receive waiting, and cannot have both + // sends and receives waiting (they would have paired off) + // but can have multiple send_type requests waiting (from diff senders) + if( waitingReq->reqType == send_type ) + { //waiting request is another send, so stack this up on list + semReq-> nextReqInHashEntry = waitingReq->nextReqInHashEntry; + waitingReq->nextReqInHashEntry = semReq; + return; + } + else + { //waiting request is a receive, so pair it to this send + //first, remove the waiting receive request from the list in entry + entry->content = waitingReq->nextReqInHashEntry; + if( entry->content == NULL ) + { //TODO: mod hash table to double-link, so can delete entry from + // table without hashing the key and looking it up again + deleteEntryFromTable( entry->key, commHashTbl ); //frees entry too + } + + //attach msg that's in this send request to receiving procr + // when comes back from suspend, will have msg in semanticData + receivePr->semanticData = semReq->msg; + + //bring both processors back from suspend + writePrivQ( sendPr, semEnv->readyVirtProcrQ ); + writePrivQ( receivePr, semEnv->readyVirtProcrQ ); + + //don't need semReq anymore -- free it + VMSHW__free_semantic_request( waitingReq ); + VMSHW__free_semantic_request( semReq ); + return; + } + } + + +/*Looks like can make single handler for both sends.. + */ +//TODO: combine both send handlers into single handler +void +handleSendFromTo( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv) + { VirtProcr *sendPr, *receivePr; + int key[] = {0,0,0}; + VMSHWSemReq *waitingReq; + HashEntry *entry; + HashTable *commHashTbl = semEnv->commHashTbl; + + receivePr = semReq->receivePr; //For "send", know both send & recv procrs + sendPr = semReq->sendPr; + + key[0] = (int)receivePr; + key[1] = (int)sendPr; + //key[2] acts at the 0 that terminates the string + + entry = lookupReqAndGiveEntryElseInsertIfEmpty( key, semReq, commHashTbl); + if( entry == NULL ) return; //was just inserted + + waitingReq = (VMSHWSemReq *)entry->content; + + //At this point, know have waiting request(s) -- either sends or recv + if( waitingReq->reqType == send_from_to ) + { printf("\n ERROR: shouldn't be two send from-tos waiting \n"); + } + else + { //waiting request is a receive, so pair it to this send + //first, remove the waiting receive request from the list in entry + entry->content = waitingReq->nextReqInHashEntry; + //can only be one waiting req for "from-to" semantics + if( entry->content != NULL ) printf("\nERROR in handleSendFromTo\n"); + deleteEntryFromTable( entry->key, commHashTbl ); //frees entry too + + //attach msg that's in this send request to receiving procr + // when comes back from suspend, will have msg in semanticData + receivePr->semanticData = semReq->msg; + + //bring both processors back from suspend + writePrivQ( sendPr, semEnv->readyVirtProcrQ ); + writePrivQ( receivePr, semEnv->readyVirtProcrQ ); + + //done with requests, so free them + VMSHW__free_semantic_request( waitingReq ); + VMSHW__free_semantic_request( semReq ); + return; + } + } + + + +//======================================================= + +/*Removed this one for now, because forces either a search or going to a + * two-level hash table, where one level the key is the receivePr, in the + * other level, the key is the type. + *So, each dest procr that either does a receive_type or that a send_type + * targets it, would have a hash table created just for it and placed + * into the first-level hash table entry for that receive procr. + *Then, doing a receive_type first looks up entry for receive procr in first + * table, gets the type-table out of that entry, and does a second lookup + * in the type-table. + *Doing a receive from-to looks up in the first table, gets the second table + * hashed on "from" procr. + *Doing a receive_any looks up in the first table, then looks to see if + * either of the hash tables have any entries -- would then have to do a + * linear search through the hash-table's array for the first non-empty + * spot + *Yuck. + * + *Alternatively, could keep two hash tables updated all the time -- one that + * does the receive_type and receive_from_to and a second that does + * receive_any -- would only hash the second table by the receive procr. + * When remove from one table, keep back-links to both tables, so can also + * quickly remove from other table. + *Cost is doing two hash-table lookups for every insert. + * If ever add receive_any, looking like this second option easier and even + * less costly. + */ +void +handleReceiveAny( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv) + { + + } + + +void +handleReceiveType( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv) + { VirtProcr *sendPr, *receivePr; + int key[] = {0,0,0}; + VMSHWSemReq *waitingReq; + HashEntry *entry; + HashTable *commHashTbl = semEnv->commHashTbl; + + receivePr = semReq->receivePr; + + key[0] = (int)receivePr; + key[1] = (int)(semReq->msgType); + //key[2] acts at the 0 that terminates the string + + + entry = lookupReqAndGiveEntryElseInsertIfEmpty( key, semReq, commHashTbl); + if( entry == NULL ) return; //was just inserted + + waitingReq = (VMSHWSemReq *)entry->content; + + //At this point, know have waiting request(s) -- should be send(s) + if( waitingReq->reqType == send_type ) + { //waiting request is a send, so pair it with this receive + //first, remove the waiting send request from the list in entry + entry->content = waitingReq->nextReqInHashEntry; + if( entry->content == NULL ) + { deleteEntryFromTable( entry->key, commHashTbl ); //frees entry too + } + + //attach msg that's in the send request to receiving procr + // when comes back from suspend, will have msg in semanticData + receivePr->semanticData = waitingReq->msg; + + //bring both processors back from suspend + writePrivQ( waitingReq->sendPr, semEnv->readyVirtProcrQ ); + writePrivQ( receivePr, semEnv->readyVirtProcrQ ); + + //done with requests, so free them + VMSHW__free_semantic_request( waitingReq ); + VMSHW__free_semantic_request( semReq ); + return; + } + printf("\nLang Impl Error: Should never be two waiting receives!\n"); + } + + +/* + */ +void +handleReceiveFromTo( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv) + { VirtProcr *sendPr, *receivePr; + int key[] = {0,0,0}; + VMSHWSemReq *waitingReq; + HashEntry *entry; + HashTable *commHashTbl = semEnv->commHashTbl; + + receivePr = semReq->receivePr; + sendPr = semReq->sendPr; //for receive from-to, know send procr + + key[0] = (int)receivePr; + key[1] = (int)sendPr; + //key[2] acts at the 0 that terminates the string + + entry = lookupReqAndGiveEntryElseInsertIfEmpty( key, semReq, commHashTbl); + if( entry == NULL ) return; //was just inserted + + waitingReq = (VMSHWSemReq *)entry->content; + + //At this point, know have waiting request(s) -- should be send(s) + if( waitingReq->reqType == send_from_to ) + { //waiting request is a send, so pair it with this receive + + //For from-to, should only ever be a single reqst waiting tobe paird + entry->content = waitingReq->nextReqInHashEntry; + if( entry->content != NULL ) printf("\nERROR in handleRecvFromTo\n"); + deleteEntryFromTable( entry->key, commHashTbl ); //frees entry too + + //attach msg that's in the send request to receiving procr + // when comes back from suspend, will have msg in semanticData + receivePr->semanticData = waitingReq->msg; + + //bring both processors back from suspend + writePrivQ( waitingReq->sendPr, semEnv->readyVirtProcrQ ); + writePrivQ( receivePr, semEnv->readyVirtProcrQ ); + + //done with requests, so free them + VMSHW__free_semantic_request( waitingReq ); + VMSHW__free_semantic_request( semReq ); + return; + } + printf("\nLang Impl Error: Should never be two waiting receives!\n"); + } + + + +//=============================================== +void +handleTransferTo( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv) + { + + } + +void +handleTransferOut( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv) + { + + } + + diff -r 35b53e6de714 -r 06ca89bafbb8 VMSHW_Request_Handlers.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VMSHW_Request_Handlers.h Wed Jul 07 13:15:29 2010 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright 2009 OpenSourceStewardshipFoundation.org + * Licensed under GNU General Public License version 2 + * + * Author: seanhalle@yahoo.com + * + */ + +#ifndef _VMSHW_REQ_H +#define _VMSHW_REQ_H + +#include "VMSHW.h" + +/*This header defines everything specific to the VMSHW semantic plug-in + */ + +void +handleSendType( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv); +void +handleSendFromTo( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv); +void +handleReceiveAny( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv); +void +handleReceiveType( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv); +void +handleReceiveFromTo( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv); +void +handleTransferTo( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv); +void +handleTransferOut( VMSHWSemReq *semReq, VMSHWSemEnv *semEnv); + +#endif /* _VMSHW_REQ_H */ + diff -r 35b53e6de714 -r 06ca89bafbb8 VMSHW_lib.c --- a/VMSHW_lib.c Sat May 22 19:33:11 2010 -0700 +++ b/VMSHW_lib.c Wed Jul 07 13:15:29 2010 -0700 @@ -8,17 +8,37 @@ #include #include -#include "../VMS/VMS.h" +#include "VMS/VMS.h" #include "VMSHW.h" -#include "../VMS/Queue_impl/NonBlockingQueue.h" +#include "VMS/Queue_impl/PrivateQueue.h" +#include "VMS/Hash_impl/PrivateHash.h" + + + +/*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 virtual + * processors (or equivalently work-units), so Master picks which virt procr + * from which portions of app (DKU, WT, FoR) onto which sched slots + *Might even do hierarchy of masters -- group of sched slots for each core + * has its own master, that keeps generated work local + * 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 + */ + + + +//=========================================================================== /*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 + * VMS_HW code. + *The VMS_HW 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. @@ -41,6 +61,47 @@ */ + +//=========================================================================== + +/*This is the "border crossing" function -- the thing that crosses from the + * outside world, into the VMS_HW world. It initializes and starts up the + * VMS system, then creates one processor from the specified function and + * puts it into the readyQ. From that point, that one function is resp. + * for creating all the other processors, that then create others, and so + * forth. + *When all the processors, including the seed, have dissipated, then this + * function returns. The results will have been written by side-effect via + * pointers read from, or written into initData. + */ +void +VMSHW__create_seed_procr_and_do_work( VirtProcrFnPtr fnPtr, void *initData ) + { VMSHWSemEnv *semEnv; + VirtProcr *seedProcr; + + VMSHW__init(); + + semEnv = _VMSMasterEnv->semanticEnv; + + //VMSHW starts with one processor, which is put into initial environ, + // and which then calls create() to create more, thereby expanding work + seedProcr = VMS__create_procr( fnPtr, initData ); + + writePrivQ( seedProcr, semEnv->readyVirtProcrQ ); + semEnv->numVirtPr = 1; + + //NOTE: no Threads should exist in the outside program that might touch + // any of the data reachable from initData given to the seed procr + VMS__start_the_work_then_wait_until_done(); + + VMSHW__shutdown(); + } + + + + +//=========================================================================== + /*Initializes all the data-structures for a VMSHW system -- but doesn't * start it running yet! * @@ -48,183 +109,248 @@ *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. + * for creating the seed processor and then starting the work. */ - void +void VMSHW__init() - { VMSHWEnv *semanticEnv; + { VMSHWSemEnv *semanticEnv; - init_VMS(); + VMS__init(); + //masterEnv, a global var, now is partially set up by init_VMS - //masterEnv, a global var, now is partially set up by init_VMS - semanticEnv = malloc( sizeof( VMSHWEnv ) ); - masterEnv->semanticEnv = semanticEnv; + //Hook up the semantic layer's plug-ins to the Master virt procr + _VMSMasterEnv->requestHandler = &VMSHW__Request_Handler; + _VMSMasterEnv->slaveScheduler = &VMSHW__schedule_virt_procr; - semanticEnv->readyWorkUnitsQ = mareadyWorkUnitsQkePrivQ(); + //create the semantic layer's environment (all its data) and add to + // the master environment + semanticEnv = malloc( sizeof( VMSHWSemEnv ) ); + _VMSMasterEnv->semanticEnv = semanticEnv; + + //create the ready queue, hash tables used for pairing send to receive + // and so forth + //TODO: add hash tables for pairing sends with receives, and + // initialize the data ownership system + semanticEnv->readyVirtProcrQ = makePrivQ(); + semanticEnv->commHashTbl = makeHashTable( 1<<16, NULL ); //start big } +/*Frees any memory allocated by VMSHW__init() then calls VMS__shutdown + */ +void +VMSHW__shutdown() + { VMSHWSemEnv *semanticEnv; + + semanticEnv = _VMSMasterEnv->semanticEnv; -/*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: double check all sem env locations freed + free( semanticEnv->readyVirtProcrQ->startOfData ); + free( semanticEnv->readyVirtProcrQ ); + freeHashTable( semanticEnv->commHashTbl ); + free( _VMSMasterEnv->semanticEnv ); + VMS__shutdown(); } -/*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 + +//=========================================================================== + +/* */ +inline VirtProcr * +VMSHW__create_procr_with( VirtProcrFnPtr fnPtr, void *initData, + VirtProcr *creatingPr ) + { VirtProcr *newPr; + newPr = VMS__create_procr( fnPtr, initData ); + //After create, have to send request to plugin for any sem env + // modifications -- such as putting the new procr into the ready Q + //Need a processor to "animate" the creation -- it's one the register + // request is attached to, and one suspended in order to send req + // to plugin + VMS__send_register_new_procr_request( newPr, creatingPr ); -/*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 ) + return newPr; + } + + +inline void +VMSHW__dissipate_procr( VirtProcr *procrToDissipate ) + { + VMS__dissipate_procr( procrToDissipate ); + } + + +//=========================================================================== + +void * +VMSHW__malloc_size_to( int numBytes, VirtProcr *ownerPr ) { } + void -VMSHW__create_seed_processor_then_wait_until_work_done - ( VMSHWProcrFnPtr fn, void *data ) - { VMSHWEnviron semEnv; +VMSHW__transfer_ownership_of_from_to( void *data, VirtProcr *oldOwnerPr, + VirtProcr *newOwnerPr ) + { - 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 ); +void +VMSHW__add_ownership_by_to( VirtProcr *newOwnerPr, void *data ) + { - //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__remove_ownership_by_from( VirtProcr *loserPr, void *dataLosing ) + { + + } + + +/*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. + * + *Must be called from the processor that currently owns the data. + * + *IMPL: Transferring ownership touches two different virtual processor's + * state -- which means it has to be done carefully -- the VMS rules for + * semantic layers say that a work-unit is only allowed to touch the + * virtual processor it is part of, and that only a single work-unit per + * virtual processor be scheduled to a slave at a time. So, this has to + * modify the virtual processor that owns the work-unit that called this + * function, then create a request to have the other processor modified. + *However, in this case, the TO processor is the outside, and transfers + * are only allowed to be called by the giver-upper, so can mark caller of + * this function as no longer owner, and return -- done. + */ +void +VMSHW__transfer_ownership_to_outside( void *data ) + { + //TODO: removeAllOwnersFrom( data ); + } + + +//=========================================================================== + +void +VMSHW__send_of_type_to( VirtProcr *sendPr, void *msg, const int type, + VirtProcr *receivePr) + { VMSHWSemReq *reqData; + + reqData = malloc( sizeof(VMSHWSemReq) ); + reqData->receivePr = receivePr; + reqData->sendPr = sendPr; + reqData->reqType = send_type; + reqData->msgType = type; + reqData->msg = msg; + + //On ownership -- remove inside the send and let ownership sit in limbo + // as a potential in an entry in the hash table, when this receive msg + // gets paired to a send, the ownership gets added to the receivePr -- + // the next work-unit in the receivePr's trace will have ownership. + VMS__add_sem_request( reqData, sendPr ); + VMS__suspend_procr( sendPr ); //will suspend then resume and continue + + //When come back from suspend, no longer own data reachable from msg + //TODO: release ownership here } void -VMSHW__create_processor( ) - { - firstWorkUnit = firstVirtProcr->latestWorkUnit; +VMSHW__send_from_to( void *msg, VirtProcr *sendPr, VirtProcr *receivePr ) + { VMSHWSemReq *reqData; - writePrivQ( firstWorkUnit, semanticEnv->readyWorkUnitsQ ); + //hash on the receiver, 'cause always know it, but sometimes want to + // receive from anonymous sender - return firstVirtProcr; + reqData = malloc( sizeof(VMSHWSemReq) ); + reqData->receivePr = receivePr; + reqData->sendPr = sendPr; + reqData->reqType = send_from_to; + reqData->msg = msg; + + //On ownership -- remove inside the send and let ownership sit in limbo + // as a potential in an entry in the hash table, when this receive msg + // gets paired to a send, the ownership gets added to the receivePr -- + // the next work-unit in the receivePr's trace will have ownership. + VMS__add_sem_request( reqData, sendPr ); + VMS__suspend_procr( sendPr ); //will suspend then resume and continue + + //When come back from suspend, no longer own data reachable from msg + //TODO: release ownership here } -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; +VMSHW__receive_any_to( VirtProcr *receivePr ) + { - //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 * +VMSHW__receive_type_to( const int type, VirtProcr *receivePr ) + { void *msg; + VMSHWSemReq *reqData; + + reqData = malloc( sizeof(VMSHWSemReq) ); + reqData->receivePr = receivePr; + reqData->reqType = receive_type; + reqData->msgType = type; + + VMS__add_sem_request( reqData, receivePr ); + VMS__suspend_procr( receivePr ); + msg = receivePr->semanticData; + return msg; + } + + + +/*Call this at point receiving virt pr wants in-coming data. + * + *The reason receivePr must call this is that it modifies the receivPr + * loc structure directly -- and the VMS rules state a virtual processor + * loc structure can only be modified by itself. + */ +void * +VMSHW__receive_from_to( VirtProcr *sendPr, VirtProcr *receivePr ) + { VMSHWSemReq *reqData; + + //hash on the receiver, 'cause always know it, but sometimes want to + // receive from anonymous sender + + reqData = malloc( sizeof(VMSHWSemReq) ); + reqData->receivePr = receivePr; + reqData->sendPr = sendPr; + reqData->reqType = receive_from_to; + + //On ownership -- remove inside the send after receive successful. + // Below, add ownership when come back from suspend + //Reason: Thinking of impl ownership mech such that it automatically + // frees any data has no owners -- so have to add receiver before + // remove sender + VMS__add_sem_request( reqData, receivePr ); + //TODO: add ownership of locs reachable from msg inside reqst handler + VMS__suspend_procr( receivePr ); //will suspend then resume and continue + + //When come back from suspend, the msg data is in receivePr->semData + return receivePr->semanticData; + } //=========================================================================== - void -freeParamStruc( ParamStruc * param ) - { if( param->type == STRING_PARAM_TYPE ) free( param->strValue ); - free( param ); + +/*Just thin wrapper for now -- semantic request is still a simple thing + * (July 3, 2010) + */ +inline void +VMSHW__free_semantic_request( VMSHWSemReq *semReq ) + { + free( semReq ); } - - 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; - }