changeset 0:4aca264971b5

Initial add -- works w/matrix mult on 9x9 but dies on larger
author Me
date Fri, 17 Sep 2010 11:34:02 -0700
parents
children 1d3157ac56c4
files DESIGN_NOTES__VPThread__lib.txt VPThread.h VPThread__PluginFns.c VPThread__Request_Handlers.c VPThread__Request_Handlers.h VPThread__lib.c
diffstat 6 files changed, 979 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/DESIGN_NOTES__VPThread__lib.txt	Fri Sep 17 11:34:02 2010 -0700
     1.3 @@ -0,0 +1,82 @@
     1.4 +
     1.5 +Implement VPThread this way:
     1.6 +
     1.7 +We implemented a subset of PThreads functionality, called VMSPThd, that
     1.8 +includes: mutex_lock, mutex_unlock, cond_wait, and cond_notify, which we name
     1.9 +as VMSPThd__mutix_lock and so forth. \ All VMSPThd functions take a reference
    1.10 +to the AppVP that is animating the function call, in addition to any other
    1.11 +parameters.
    1.12 +
    1.13 +A mutex variable is an integer, returned by VMSPThd__mutex_create(), which is
    1.14 +used inside the request handler as a key to lookup an entry in a hash table,
    1.15 +that lives in the SemanticEnv. \ Such an entry has a field holding a
    1.16 +reference to the AppVP that currently owns the lock, and a queue of AppVPs
    1.17 +waiting to acquire the lock. \
    1.18 +
    1.19 +Acquiring a lock is done with VMSPThd__mutex_lock(), which generates a
    1.20 +request. \ Recall that all request sends cause the suspention of the AppVP
    1.21 +that is animating the library call that generates the request, in this case
    1.22 +the AppVP animating VMSPThd__mutex_lock() is suspended. \ The request
    1.23 +includes a reference to that animating AppVP, and the mutex integer value.
    1.24 +\ When the request reaches the request handler, the mutex integer is used as
    1.25 +key to look up the hash entry, then if the owner field is null (or the same
    1.26 +as the AppVP in the request), the AppVP in the request is placed into the
    1.27 +owner field, and that AppVP is queued to be scheduled for re-animation.
    1.28 +\ However, if a different AppVP is listed in the owner field, then the AppVP
    1.29 +in the request is added to the queue of those trying to acquire. \ Notice
    1.30 +that this is a purely sequential algorithm that systematic reasoning can be
    1.31 +used on.
    1.32 +
    1.33 +VMSPThd__mutex_unlock(), meanwhile, generates a request that causes the
    1.34 +request handler to queue for re-animation the AppVP that animated the call.
    1.35 +\ It also pops the queue of AppVPs waiting to acquire the lock, and writes
    1.36 +the AppVP that comes out as the current owner of the lock and queues that
    1.37 +AppVP for re-animation (unless the popped value is null, in which case the
    1.38 +current owner is just set to null).
    1.39 +
    1.40 +Implementing condition variables takes a similar approach, in that
    1.41 +VMSPThd__init_cond() returns an integer that is then used to look up an entry
    1.42 +in a hash table, where the entry contains a queue of AppVPs waiting on the
    1.43 +condition variable. \ VMSPThd__cond_wait() generates a request that pushes
    1.44 +the AppVP into the queue, while VMSPThd__cond_signal() takes a wait request
    1.45 +from the queue.
    1.46 +
    1.47 +Notice that this is again a purely sequential algorithm, and sidesteps issues
    1.48 +such as ``simultaneous'' wait and signal requests -- the wait and signal get
    1.49 +serialized automatically, even though they take place at the same instant of
    1.50 +program virtual time. \
    1.51 +
    1.52 +It is the fact of having a program virtual time that allows ``virtual
    1.53 +simultaneous'' actions to be handled <em|outside> of the virtual time. \ That
    1.54 +ability to escape outside of the virtual time is what enables a
    1.55 +<em|sequential> algorithm to handle the simultaneity that is at the heart of
    1.56 +making implementing locks in physical time so intricately tricky
    1.57 +<inactive|<cite|LamportLockImpl>> <inactive|<cite|DijkstraLockPaper>>
    1.58 +<inactive|<cite|LamportRelativisticTimePaper>>.\
    1.59 +
    1.60 +What's nice about this approach is that the design and implementation are
    1.61 +simple and straight forward. \ It took just X days to design, implement, and
    1.62 +debug, and is in a form that should be amenable to proof of freedom from race
    1.63 +conditions, given a correct implementation of VMS. \ The hash-table based
    1.64 +approach also makes it reasonably high performance, with (essentially) no
    1.65 +slowdown when the number of locks or number of AppVPs grows large.
    1.66 +
    1.67 +===========================
    1.68 +Behavior:
    1.69 +Cond variables are half of a two-piece mechanism.  The other half is a mutex.
    1.70 + Every cond var owns a mutex -- the two intrinsically work
    1.71 + together, as a pair.  The mutex must only be used with the condition var
    1.72 + and not used on its own in other ways.
    1.73 +
    1.74 +cond_wait is called with a cond-var and its mutex.
    1.75 +The animating processor must have acquired the mutex before calling cond_wait
    1.76 +The call adds the animating processor to the queue associated with the cond
    1.77 +variable and then calls mutex_unlock on the mutex.
    1.78 +
    1.79 +cond_signal can only be called after acquiring the cond var's mutex.  It is
    1.80 +called with the cond-var.
    1.81 + The call takes the next processor from the condition-var's wait queue and
    1.82 + transfers it to the waiting-for-lock queue of the cond-var's mutex.
    1.83 +The processor that called the cond_signal next has to perform a mutex_unlock
    1.84 + on the cond-var's mutex -- that, finally, lets the waiting processor acquire
    1.85 + the mutex and proceed.
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/VPThread.h	Fri Sep 17 11:34:02 2010 -0700
     2.3 @@ -0,0 +1,153 @@
     2.4 +/*
     2.5 + *  Copyright 2009 OpenSourceStewardshipFoundation.org
     2.6 + *  Licensed under GNU General Public License version 2
     2.7 + *
     2.8 + * Author: seanhalle@yahoo.com
     2.9 + *
    2.10 + */
    2.11 +
    2.12 +#ifndef _VPThread_H
    2.13 +#define	_VPThread_H
    2.14 +
    2.15 +#include "VMS/VMS.h"
    2.16 +#include "VMS/Queue_impl/PrivateQueue.h"
    2.17 +#include "VMS/DynArray/DynArray.h"
    2.18 +
    2.19 +
    2.20 +//===========================================================================
    2.21 +#define INIT_NUM_MUTEX 10000
    2.22 +#define INIT_NUM_COND  10000
    2.23 +//===========================================================================
    2.24 +
    2.25 +/*This header defines everything specific to the VPThread semantic plug-in
    2.26 + */
    2.27 +typedef struct _VPThreadSemReq   VPThreadSemReq;
    2.28 +
    2.29 +
    2.30 +/*Semantic layer-specific data sent inside a request from lib called in app
    2.31 + * to request handler called in MasterLoop
    2.32 + */
    2.33 +enum VPThreadReqType
    2.34 + {
    2.35 +   make_mutex = 1,
    2.36 +   mutex_lock,
    2.37 +   mutex_unlock,
    2.38 +   make_cond,
    2.39 +   cond_wait,
    2.40 +   cond_signal,
    2.41 +   make_procr
    2.42 + };
    2.43 +
    2.44 +struct _VPThreadSemReq
    2.45 + { enum VPThreadReqType reqType;
    2.46 +   VirtProcr           *requestingPr;
    2.47 +   int32                mutexIdx;
    2.48 +   int32                condIdx;
    2.49 +   void                *initData;
    2.50 +   VirtProcrFnPtr       fnPtr;
    2.51 + }
    2.52 +/* VPThreadSemReq */;
    2.53 +
    2.54 +typedef struct
    2.55 + {
    2.56 +      //Standard stuff will be in most every semantic env
    2.57 +   PrivQueueStruc **readyVPQs;
    2.58 +   int32            numVirtPr;
    2.59 +   int32            nextCoreToGetNewPr;
    2.60 +
    2.61 +      //Specific to this semantic layer
    2.62 +   int32            currMutexIdx;
    2.63 +   DynArray32      *mutexDynArray;
    2.64 +   
    2.65 +   int32            currCondIdx;
    2.66 +   DynArray32      *condDynArray;
    2.67 +
    2.68 +   void            *applicationGlobals;
    2.69 + }
    2.70 +VPThreadSemEnv;
    2.71 +
    2.72 +
    2.73 +typedef struct
    2.74 + {
    2.75 +   int32           mutexIdx;
    2.76 +   VirtProcr      *holderOfLock;
    2.77 +   PrivQueueStruc *waitingQueue;
    2.78 + }
    2.79 +VPTMutex;
    2.80 +
    2.81 +
    2.82 +typedef struct
    2.83 + {
    2.84 +   int32           condIdx;
    2.85 +   PrivQueueStruc *waitingQueue;
    2.86 +   VPTMutex       *partnerMutex;
    2.87 + }
    2.88 +VPTCond;
    2.89 +
    2.90 +
    2.91 +//===========================================================================
    2.92 +
    2.93 +void
    2.94 +VPThread__create_seed_procr_and_do_work( VirtProcrFnPtr fn, void *initData );
    2.95 +
    2.96 +//=======================
    2.97 +
    2.98 +inline VirtProcr *
    2.99 +VPThread__create_thread( VirtProcrFnPtr fnPtr, void *initData,
   2.100 +                          VirtProcr *creatingPr );
   2.101 +
   2.102 +void
   2.103 +VPThread__dissipate_thread( VirtProcr *procrToDissipate );
   2.104 +
   2.105 +//=======================
   2.106 +void
   2.107 +VPThread__set_globals_to( void *globals );
   2.108 +
   2.109 +void *
   2.110 +VPThread__give_globals();
   2.111 +
   2.112 +//=======================
   2.113 +int32
   2.114 +VPThread__make_mutex( VirtProcr *animPr );
   2.115 +
   2.116 +void
   2.117 +VPThread__mutex_lock( int32 mutexIdx, VirtProcr *acquiringPr );
   2.118 +                                                    
   2.119 +void
   2.120 +VPThread__mutex_unlock( int32 mutexIdx, VirtProcr *releasingPr );
   2.121 +
   2.122 +
   2.123 +//=======================
   2.124 +int32
   2.125 +VPThread__make_cond( int32 ownedMutexIdx, VirtProcr *animPr);
   2.126 +
   2.127 +void
   2.128 +VPThread__cond_wait( int32 condIdx, VirtProcr *waitingPr);
   2.129 +
   2.130 +void *
   2.131 +VPThread__cond_signal( int32 condIdx, VirtProcr *signallingPr );
   2.132 +
   2.133 +
   2.134 +
   2.135 +
   2.136 +//=========================  Internal use only  =============================
   2.137 +void
   2.138 +VPThread__Request_Handler( VirtProcr *requestingPr, void *_semEnv );
   2.139 +
   2.140 +VirtProcr *
   2.141 +VPThread__schedule_virt_procr( void *_semEnv, int coreNum );
   2.142 +
   2.143 +//=======================
   2.144 +void
   2.145 +VPThread__free_semantic_request( VPThreadSemReq *semReq );
   2.146 +
   2.147 +//=======================
   2.148 +
   2.149 +void
   2.150 +VPThread__init();
   2.151 +
   2.152 +void
   2.153 +VPThread__cleanup_after_shutdown();
   2.154 +
   2.155 +#endif	/* _VPThread_H */
   2.156 +
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/VPThread__PluginFns.c	Fri Sep 17 11:34:02 2010 -0700
     3.3 @@ -0,0 +1,110 @@
     3.4 +/*
     3.5 + * Copyright 2010  OpenSourceCodeStewardshipFoundation
     3.6 + *
     3.7 + * Licensed under BSD
     3.8 + */
     3.9 +
    3.10 +#include <stdio.h>
    3.11 +#include <stdlib.h>
    3.12 +#include <malloc.h>
    3.13 +
    3.14 +#include "VMS/Queue_impl/PrivateQueue.h"
    3.15 +#include "VPThread.h"
    3.16 +#include "VPThread__Request_Handlers.h"
    3.17 +
    3.18 +
    3.19 +/*Will get requests to send, to receive, and to create new processors.
    3.20 + * Upon send, check the hash to see if a receive is waiting.
    3.21 + * Upon receive, check hash to see if a send has already happened.
    3.22 + * When other is not there, put in.  When other is there, the comm.
    3.23 + *  completes, which means the receiver P gets scheduled and
    3.24 + *  picks up right after the receive request.  So make the work-unit
    3.25 + *  and put it into the queue of work-units ready to go.
    3.26 + * Other request is create a new Processor, with the function to run in the
    3.27 + *  Processor, and initial data.
    3.28 + */
    3.29 +void
    3.30 +VPThread__Request_Handler( VirtProcr *requestingPr, void *_semEnv )
    3.31 + { VPThreadSemEnv *semEnv;
    3.32 +   VMSReqst    *req;
    3.33 +   VPThreadSemReq *semReq;
    3.34 + 
    3.35 +   semEnv = (VPThreadSemEnv *)_semEnv;
    3.36 +
    3.37 +   req = VMS__take_top_request_from( requestingPr );
    3.38 +   
    3.39 +   while( req != NULL )
    3.40 +    {
    3.41 +      if( VMS__isSemanticReqst( req ) )
    3.42 +       {
    3.43 +         semReq = VMS__take_sem_reqst_from( req );
    3.44 +         if( semReq == NULL ) goto DoneHandlingReqst;
    3.45 +         switch( semReq->reqType )
    3.46 +          {
    3.47 +            case make_mutex:     handleMakeMutex(  semReq, semEnv);
    3.48 +               break;
    3.49 +            case mutex_lock:     handleMutexLock(  semReq, semEnv);
    3.50 +               break;
    3.51 +            case mutex_unlock:   handleMutexUnlock(semReq, semEnv);
    3.52 +               break;
    3.53 +            case make_cond:      handleMakeCond(   semReq, semEnv);
    3.54 +               break;
    3.55 +            case cond_wait:      handleCondWait(   semReq, semEnv);
    3.56 +               break;
    3.57 +            case cond_signal:    handleCondSignal( semReq, semEnv);
    3.58 +               //need? VPThread__free_semantic_request( semReq );
    3.59 +               break;
    3.60 +            case make_procr:     handleMakeProcr(  semReq, semEnv);
    3.61 +               break;
    3.62 +            //TODO: make sure free the semantic request!
    3.63 +          }
    3.64 +         //NOTE: freeing semantic request data strucs handled inside these
    3.65 +       }
    3.66 +      else if( VMS__isDissipateReqst( req ) ) //Standard VMS request
    3.67 +       {    //Another standard VMS request that the plugin has to handle
    3.68 +            //This time, plugin has to free the semantic data it may have
    3.69 +            // allocated into the virt procr -- and clear the AppVP out of
    3.70 +            // any data structs the plug-in may have put it into, like hash
    3.71 +            // tables.
    3.72 +
    3.73 +            //Now, call VMS to free all AppVP state -- stack and so on
    3.74 +         VMS__handle_dissipate_reqst( requestingPr );
    3.75 +
    3.76 +            //Keep count of num AppVPs, so know when to shutdown
    3.77 +         semEnv->numVirtPr -= 1;
    3.78 +         if( semEnv->numVirtPr == 0 )
    3.79 +          {    //no more work, so shutdown
    3.80 +            VMS__handle_shutdown_reqst( requestingPr );
    3.81 +          }
    3.82 +       }
    3.83 +
    3.84 +      DoneHandlingReqst:
    3.85 +         //Here, free VMS's request structure, no matter what -- even though
    3.86 +         // semantic request struc instances may still be around..
    3.87 +         //This call frees VMS's portion, then returns the next request
    3.88 +      req = VMS__free_top_and_give_next_request_from( requestingPr );
    3.89 +    } //while( req != NULL )
    3.90 + }
    3.91 +
    3.92 +//===========================================================================
    3.93 +
    3.94 +
    3.95 +/*For VPThread, scheduling a slave simply takes the next work-unit off the
    3.96 + * ready-to-go work-unit queue and assigns it to the slaveToSched.
    3.97 + *If the ready-to-go work-unit queue is empty, then nothing to schedule
    3.98 + * to the slave -- return FALSE to let Master loop know scheduling that
    3.99 + * slave failed.
   3.100 + */
   3.101 +VirtProcr *
   3.102 +VPThread__schedule_virt_procr( void *_semEnv, int coreNum )
   3.103 + { VirtProcr   *schedPr;
   3.104 +   VPThreadSemEnv *semEnv;
   3.105 +
   3.106 +   semEnv = (VPThreadSemEnv *)_semEnv;
   3.107 +
   3.108 +   schedPr = readPrivQ( semEnv->readyVPQs[coreNum] );
   3.109 +      //Note, using a non-blocking queue -- it returns NULL if queue empty
   3.110 +
   3.111 +   return( schedPr );
   3.112 + }
   3.113 +
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/VPThread__Request_Handlers.c	Fri Sep 17 11:34:02 2010 -0700
     4.3 @@ -0,0 +1,222 @@
     4.4 +/*
     4.5 + * Copyright 2010  OpenSourceCodeStewardshipFoundation
     4.6 + *
     4.7 + * Licensed under BSD
     4.8 + */
     4.9 +
    4.10 +#include <stdio.h>
    4.11 +#include <stdlib.h>
    4.12 +#include <malloc.h>
    4.13 +
    4.14 +#include "VMS/VMS.h"
    4.15 +#include "VMS/Queue_impl/PrivateQueue.h"
    4.16 +#include "VMS/Hash_impl/PrivateHash.h"
    4.17 +#include "VPThread.h"
    4.18 +
    4.19 +
    4.20 +
    4.21 +//===============================  Mutexes  =================================
    4.22 +/*The semantic request has a mutexIdx value, which acts as index into array.
    4.23 + */
    4.24 +void
    4.25 +handleMakeMutex( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv)
    4.26 + { VPTMutex  *newMutex;
    4.27 +   VirtProcr  *pr;
    4.28 +   int32       mutexIdx;
    4.29 +
    4.30 +   newMutex = malloc( sizeof(VPTMutex) );
    4.31 +   newMutex->waitingQueue = makePrivQ();
    4.32 +   newMutex->holderOfLock = NULL;
    4.33 +   newMutex->mutexIdx = semEnv->currMutexIdx++;
    4.34 +   mutexIdx = newMutex->mutexIdx;
    4.35 +
    4.36 +      //The mutex struc contains an int that identifies it -- use that as
    4.37 +      // its index within the array of mutexes.  Add the new mutex to array.
    4.38 +   makeArray32BigEnoughForIndex( semEnv->mutexDynArray, mutexIdx );
    4.39 +   semEnv->mutexDynArray->array[ mutexIdx ] = newMutex;
    4.40 +
    4.41 +      //Now communicate the mutex's identifying int back to requesting procr
    4.42 +   semReq->requestingPr->semanticData = newMutex->mutexIdx;
    4.43 +
    4.44 +      //re-animate the requester
    4.45 +   pr = semReq->requestingPr;
    4.46 +   writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
    4.47 + }
    4.48 +
    4.49 +
    4.50 +void
    4.51 +handleMutexLock( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv)
    4.52 + { VPTMutex  *mutex;
    4.53 +   VirtProcr  *pr;
    4.54 +
    4.55 +   //===================  Deterministic Replay  ======================
    4.56 +   #ifdef RECORD_DETERMINISTIC_REPLAY
    4.57 +   
    4.58 +   #endif
    4.59 +   //=================================================================
    4.60 +      //lookup mutex struc, using mutexIdx as index
    4.61 +   mutex = semEnv->mutexDynArray->array[ semReq->mutexIdx ];
    4.62 +
    4.63 +      //see if mutex is free or not
    4.64 +   if( mutex->holderOfLock == NULL ) //none holding, give lock to requester
    4.65 +    {
    4.66 +      mutex->holderOfLock = semReq->requestingPr;
    4.67 +
    4.68 +         //re-animate requester, now that it has the lock
    4.69 +      pr = semReq->requestingPr;
    4.70 +      writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
    4.71 +    }
    4.72 +   else //queue up requester to wait for release of lock
    4.73 +    {
    4.74 +      writePrivQ( semReq->requestingPr, mutex->waitingQueue );
    4.75 +    }
    4.76 + }
    4.77 +
    4.78 +/*
    4.79 + */
    4.80 +void
    4.81 +handleMutexUnlock( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv)
    4.82 + { VPTMutex  *mutex;
    4.83 +   VirtProcr  *pr;
    4.84 +
    4.85 +      //lookup mutex struc, using mutexIdx as index
    4.86 +   mutex = semEnv->mutexDynArray->array[ semReq->mutexIdx ];
    4.87 +
    4.88 +      //set new holder of mutex-lock to be next in queue (NULL if empty)
    4.89 +   mutex->holderOfLock = readPrivQ( mutex->waitingQueue );
    4.90 +
    4.91 +      //if have new non-NULL holder, re-animate it
    4.92 +   if( mutex->holderOfLock != NULL )
    4.93 +    {
    4.94 +      pr = mutex->holderOfLock;
    4.95 +      writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
    4.96 +    }
    4.97 +
    4.98 +      //re-animate the releaser of the lock
    4.99 +   pr = semReq->requestingPr;
   4.100 +   writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
   4.101 + }
   4.102 +
   4.103 +//===========================  Condition Vars  ==============================
   4.104 +/*The semantic request has the cond-var value and mutex value, which are the
   4.105 + * indexes into the array.  Not worrying about having too many mutexes or
   4.106 + * cond vars created, so using array instead of hash table, for speed.
   4.107 + */
   4.108 +
   4.109 +
   4.110 +/*Make cond has to be called with the mutex that the cond is paired to
   4.111 + * Don't have to implement this way, but was confusing learning cond vars
   4.112 + * until deduced that each cond var owns a mutex that is used only for
   4.113 + * interacting with that cond var.  So, make this pairing explicit.
   4.114 + */
   4.115 +void
   4.116 +handleMakeCond( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv)
   4.117 + { VPTCond   *newCond;
   4.118 +   VirtProcr  *pr;
   4.119 +   int32       condIdx;
   4.120 +
   4.121 +   newCond = malloc( sizeof(VPTCond) );
   4.122 +   newCond->partnerMutex = semEnv->mutexDynArray->array[ semReq->mutexIdx ];
   4.123 +
   4.124 +   newCond->waitingQueue = makePrivQ();
   4.125 +   newCond->condIdx = semEnv->currCondIdx++;
   4.126 +   condIdx = newCond->condIdx;
   4.127 +
   4.128 +      //The cond struc contains an int that identifies it -- use that as
   4.129 +      // its index within the array of conds.  Add the new cond to array.
   4.130 +   makeArray32BigEnoughForIndex( semEnv->condDynArray, condIdx );
   4.131 +   semEnv->condDynArray->array[ condIdx ] = newCond;
   4.132 +
   4.133 +      //Now communicate the cond's identifying int back to requesting procr
   4.134 +   semReq->requestingPr->semanticData = newCond->condIdx;
   4.135 +   
   4.136 +      //re-animate the requester
   4.137 +   pr = semReq->requestingPr;
   4.138 +   writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
   4.139 + }
   4.140 +
   4.141 +
   4.142 +/*Mutex has already been paired to the cond var, so don't need to send the
   4.143 + * mutex, just the cond var.  Don't have to do this, but want to bitch-slap
   4.144 + * the designers of Posix standard  ; )
   4.145 + */
   4.146 +void
   4.147 +handleCondWait( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv)
   4.148 + { VPTCond   *cond;
   4.149 +   VPTMutex  *mutex;
   4.150 +   VirtProcr  *pr;
   4.151 +
   4.152 +      //get cond struc out of array of them that's in the sem env
   4.153 +   cond = semEnv->condDynArray->array[ semReq->condIdx ];
   4.154 +
   4.155 +      //add requester to queue of wait-ers
   4.156 +   writePrivQ( semReq->requestingPr, cond->waitingQueue );
   4.157 +    
   4.158 +      //unlock mutex -- can't reuse above handler 'cause not queuing releaser
   4.159 +   mutex = cond->partnerMutex;
   4.160 +   mutex->holderOfLock = readPrivQ( mutex->waitingQueue );
   4.161 +
   4.162 +   if( mutex->holderOfLock != NULL )
   4.163 +    {
   4.164 +      pr = mutex->holderOfLock;
   4.165 +      writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
   4.166 +   }
   4.167 + }
   4.168 +
   4.169 +
   4.170 +/*Note that have to implement this such that guarantee the waiter is the one
   4.171 + * that gets the lock
   4.172 + */
   4.173 +void
   4.174 +handleCondSignal( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv)
   4.175 + { VPTCond   *cond;
   4.176 +   VPTMutex  *mutex;
   4.177 +   VirtProcr  *waitingPr, *pr;
   4.178 +
   4.179 +      //get cond struc out of array of them that's in the sem env
   4.180 +   cond = semEnv->condDynArray->array[ semReq->condIdx ];
   4.181 +   
   4.182 +      //take next waiting procr out of queue
   4.183 +   waitingPr = readPrivQ( cond->waitingQueue );
   4.184 +
   4.185 +      //transfer waiting procr to wait queue of mutex
   4.186 +      // mutex is guaranteed to be held by signalling procr, so no check
   4.187 +   mutex = cond->partnerMutex;
   4.188 +   pushPrivQ( waitingPr, mutex->waitingQueue ); //is first out when read
   4.189 +
   4.190 +      //re-animate the signalling procr
   4.191 +   pr = semReq->requestingPr;
   4.192 +   writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
   4.193 + }
   4.194 +
   4.195 +
   4.196 +
   4.197 +/*Make cond has to be called with the mutex that the cond is paired to
   4.198 + * Don't have to implement this way, but was confusing learning cond vars
   4.199 + * until deduced that each cond var owns a mutex that is used only for
   4.200 + * interacting with that cond var.  So, make this pairing explicit.
   4.201 + */
   4.202 +void
   4.203 +handleMakeProcr( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv)
   4.204 + { VirtProcr  *newPr, *pr;
   4.205 +
   4.206 +   newPr = VMS__create_procr( semReq->fnPtr, semReq->initData );
   4.207 +
   4.208 +   semEnv->numVirtPr += 1;
   4.209 +
   4.210 +      //Assign new processor to next core in line & queue it up
   4.211 +   #ifdef SEQUENTIAL
   4.212 +   newPr->coreAnimatedBy = 0;
   4.213 +   #else
   4.214 +   newPr->coreAnimatedBy = semEnv->nextCoreToGetNewPr;
   4.215 +   if( semEnv->nextCoreToGetNewPr >= NUM_CORES - 1 )
   4.216 +       semEnv->nextCoreToGetNewPr  = 0;
   4.217 +   else
   4.218 +       semEnv->nextCoreToGetNewPr += 1;
   4.219 +   #endif
   4.220 +   writePrivQ( newPr, semEnv->readyVPQs[newPr->coreAnimatedBy] );
   4.221 +
   4.222 +      //re-animate the requester
   4.223 +   pr = semReq->requestingPr;
   4.224 +   writePrivQ( pr,    semEnv->readyVPQs[pr->coreAnimatedBy] );
   4.225 + }
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/VPThread__Request_Handlers.h	Fri Sep 17 11:34:02 2010 -0700
     5.3 @@ -0,0 +1,33 @@
     5.4 +/*
     5.5 + *  Copyright 2009 OpenSourceStewardshipFoundation.org
     5.6 + *  Licensed under GNU General Public License version 2
     5.7 + *
     5.8 + * Author: seanhalle@yahoo.com
     5.9 + *
    5.10 + */
    5.11 +
    5.12 +#ifndef _VPThread_REQ_H
    5.13 +#define	_VPThread_REQ_H
    5.14 +
    5.15 +#include "VPThread.h"
    5.16 +
    5.17 +/*This header defines everything specific to the VPThread semantic plug-in
    5.18 + */
    5.19 +
    5.20 +void
    5.21 +handleMakeMutex(  VPThreadSemReq *semReq, VPThreadSemEnv *semEnv);
    5.22 +void
    5.23 +handleMutexLock(  VPThreadSemReq *semReq, VPThreadSemEnv *semEnv);
    5.24 +void
    5.25 +handleMutexUnlock(VPThreadSemReq *semReq, VPThreadSemEnv *semEnv);
    5.26 +void
    5.27 +handleMakeCond(   VPThreadSemReq *semReq, VPThreadSemEnv *semEnv);
    5.28 +void
    5.29 +handleCondWait(   VPThreadSemReq *semReq, VPThreadSemEnv *semEnv);
    5.30 +void
    5.31 +handleCondSignal( VPThreadSemReq *semReq, VPThreadSemEnv *semEnv);
    5.32 +void
    5.33 +handleMakeProcr(  VPThreadSemReq *semReq, VPThreadSemEnv *semEnv);
    5.34 +
    5.35 +#endif	/* _VPThread_REQ_H */
    5.36 +
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/VPThread__lib.c	Fri Sep 17 11:34:02 2010 -0700
     6.3 @@ -0,0 +1,379 @@
     6.4 +/*
     6.5 + * Copyright 2010  OpenSourceCodeStewardshipFoundation
     6.6 + *
     6.7 + * Licensed under BSD
     6.8 + */
     6.9 +
    6.10 +#include <stdio.h>
    6.11 +#include <stdlib.h>
    6.12 +#include <malloc.h>
    6.13 +
    6.14 +#include "VMS/VMS.h"
    6.15 +#include "VPThread.h"
    6.16 +#include "VMS/Queue_impl/PrivateQueue.h"
    6.17 +#include "VMS/Hash_impl/PrivateHash.h"
    6.18 +
    6.19 +
    6.20 +//==========================================================================
    6.21 +
    6.22 +void
    6.23 +VPThread__init();
    6.24 +
    6.25 +void
    6.26 +VPThread__init_Seq();
    6.27 +
    6.28 +void
    6.29 +VPThread__init_Helper();
    6.30 +//==========================================================================
    6.31 +
    6.32 +
    6.33 +/*TODO: Q: dealing with library f()s and DKU vs WT vs FoR
    6.34 + * (still want to do FoR, with time-lines as syntax, could be super cool)
    6.35 + * A: thinking pin the coreLoops for all of BLIS -- let Master arbitrate
    6.36 + * among library, DKU, WT, FoR -- all the patterns in terms of virtual
    6.37 + * processors (or equivalently work-units), so Master picks which virt procr
    6.38 + * from which portions of app (DKU, WT, FoR) onto which sched slots
    6.39 + *Might even do hierarchy of masters -- group of sched slots for each core
    6.40 + * has its own master, that keeps generated work local
    6.41 + * single-reader-single-writer sync everywhere -- no atomic primitives
    6.42 + * Might have the different schedulers talk to each other, to negotiate
    6.43 + * larger-grain sharing of resources, according to predicted critical
    6.44 + * path, and expansion of work
    6.45 + */
    6.46 +
    6.47 +
    6.48 +
    6.49 +//===========================================================================
    6.50 +
    6.51 +
    6.52 +/*These are the library functions *called in the application*
    6.53 + * 
    6.54 + *There's a pattern for the outside sequential code to interact with the
    6.55 + * VMS_HW code.
    6.56 + *The VMS_HW system is inside a boundary..  every VPThread system is in its
    6.57 + * own directory that contains the functions for each of the processor types.
    6.58 + * One of the processor types is the "seed" processor that starts the
    6.59 + * cascade of creating all the processors that do the work.
    6.60 + *So, in the directory is a file called "EntryPoint.c" that contains the
    6.61 + * function, named appropriately to the work performed, that the outside
    6.62 + * sequential code calls.  This function follows a pattern:
    6.63 + *1) it calls VPThread__init()
    6.64 + *2) it creates the initial data for the seed processor, which is passed
    6.65 + *    in to the function
    6.66 + *3) it creates the seed VPThread processor, with the data to start it with.
    6.67 + *4) it calls startVPThreadThenWaitUntilWorkDone
    6.68 + *5) it gets the returnValue from the transfer struc and returns that
    6.69 + *    from the function
    6.70 + *
    6.71 + *For now, a new VPThread system has to be created via VPThread__init every
    6.72 + * time an entry point function is called -- later, might add letting the
    6.73 + * VPThread system be created once, and let all the entry points just reuse
    6.74 + * it -- want to be as simple as possible now, and see by using what makes
    6.75 + * sense for later..
    6.76 + */
    6.77 +
    6.78 +
    6.79 +
    6.80 +//===========================================================================
    6.81 +
    6.82 +/*This is the "border crossing" function -- the thing that crosses from the
    6.83 + * outside world, into the VMS_HW world.  It initializes and starts up the
    6.84 + * VMS system, then creates one processor from the specified function and
    6.85 + * puts it into the readyQ.  From that point, that one function is resp.
    6.86 + * for creating all the other processors, that then create others, and so
    6.87 + * forth.
    6.88 + *When all the processors, including the seed, have dissipated, then this
    6.89 + * function returns.  The results will have been written by side-effect via
    6.90 + * pointers read from, or written into initData.
    6.91 + *
    6.92 + *NOTE: no Threads should exist in the outside program that might touch
    6.93 + * any of the data reachable from initData passed in to here
    6.94 + */
    6.95 +void
    6.96 +VPThread__create_seed_procr_and_do_work( VirtProcrFnPtr fnPtr, void *initData )
    6.97 + { VPThreadSemEnv *semEnv;
    6.98 +   VirtProcr *seedPr;
    6.99 +
   6.100 +   #ifdef SEQUENTIAL
   6.101 +   VPThread__init_Seq();  //debug sequential exe
   6.102 +   #else
   6.103 +   VPThread__init();      //normal multi-thd
   6.104 +   #endif
   6.105 +   semEnv = _VMSMasterEnv->semanticEnv;
   6.106 +
   6.107 +      //VPThread starts with one processor, which is put into initial environ,
   6.108 +      // and which then calls create() to create more, thereby expanding work
   6.109 +   seedPr = VMS__create_procr( fnPtr, initData );
   6.110 +
   6.111 +   seedPr->coreAnimatedBy = semEnv->nextCoreToGetNewPr++;
   6.112 +
   6.113 +   writePrivQ( seedPr, semEnv->readyVPQs[seedPr->coreAnimatedBy] );
   6.114 +   semEnv->numVirtPr = 1;
   6.115 +
   6.116 +   #ifdef SEQUENTIAL
   6.117 +   VMS__start_the_work_then_wait_until_done_Seq();  //debug sequential exe
   6.118 +   #else
   6.119 +   VMS__start_the_work_then_wait_until_done();      //normal multi-thd
   6.120 +   #endif
   6.121 +
   6.122 +   VPThread__cleanup_after_shutdown();
   6.123 + }
   6.124 +
   6.125 +
   6.126 +//===========================================================================
   6.127 +
   6.128 +/*Initializes all the data-structures for a VPThread system -- but doesn't
   6.129 + * start it running yet!
   6.130 + *
   6.131 + * 
   6.132 + *This sets up the semantic layer over the VMS system
   6.133 + *
   6.134 + *First, calls VMS_Setup, then creates own environment, making it ready
   6.135 + * for creating the seed processor and then starting the work.
   6.136 + */
   6.137 +void
   6.138 +VPThread__init()
   6.139 + {
   6.140 +   VMS__init();
   6.141 +      //masterEnv, a global var, now is partially set up by init_VMS
   6.142 +
   6.143 +   VPThread__init_Helper();
   6.144 + }
   6.145 +
   6.146 +void
   6.147 +VPThread__init_Seq()
   6.148 + {
   6.149 +   VMS__init_Seq();
   6.150 +      //masterEnv, a global var, now is partially set up by init_VMS
   6.151 +
   6.152 +   VPThread__init_Helper();
   6.153 + }
   6.154 +
   6.155 +void
   6.156 +VPThread__init_Helper()
   6.157 + { VPThreadSemEnv       *semanticEnv;
   6.158 +   PrivQueueStruc **readyVPQs;
   6.159 +   int              coreIdx;
   6.160 + 
   6.161 +      //Hook up the semantic layer's plug-ins to the Master virt procr
   6.162 +   _VMSMasterEnv->requestHandler = &VPThread__Request_Handler;
   6.163 +   _VMSMasterEnv->slaveScheduler = &VPThread__schedule_virt_procr;
   6.164 +
   6.165 +      //create the semantic layer's environment (all its data) and add to
   6.166 +      // the master environment
   6.167 +   semanticEnv = malloc( sizeof( VPThreadSemEnv ) );
   6.168 +   _VMSMasterEnv->semanticEnv = semanticEnv;
   6.169 +
   6.170 +      //create the ready queue
   6.171 +   readyVPQs = malloc( NUM_CORES * sizeof(PrivQueueStruc *) );
   6.172 +
   6.173 +   for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ )
   6.174 +    {
   6.175 +      readyVPQs[ coreIdx ] = makePrivQ();
   6.176 +    }
   6.177 +   
   6.178 +   semanticEnv->readyVPQs          = readyVPQs;
   6.179 +   
   6.180 +   semanticEnv->numVirtPr          = 0;
   6.181 +   semanticEnv->nextCoreToGetNewPr = 0;
   6.182 +
   6.183 +   semanticEnv->currMutexIdx       = 0;
   6.184 +   semanticEnv->mutexDynArray      = createDynArray32( INIT_NUM_MUTEX );
   6.185 +
   6.186 +   semanticEnv->currCondIdx        = 0;
   6.187 +   semanticEnv->condDynArray       = createDynArray32( INIT_NUM_COND );
   6.188 + }
   6.189 +
   6.190 +
   6.191 +/*Frees any memory allocated by VPThread__init() then calls VMS__shutdown
   6.192 + */
   6.193 +void
   6.194 +VPThread__cleanup_after_shutdown()
   6.195 + { VPThreadSemEnv *semEnv;
   6.196 +   int32           coreIdx,     idx,   highestIdx;
   6.197 +   VPTMutex      **mutexArray, *mutex;
   6.198 +   VPTCond       **condArray, *cond;
   6.199 + 
   6.200 +   semEnv = _VMSMasterEnv->semanticEnv;
   6.201 +
   6.202 +//TODO: double check that all sem env locations freed
   6.203 +
   6.204 +   for( coreIdx = 0; coreIdx < NUM_CORES; coreIdx++ )
   6.205 +    {
   6.206 +      free( semEnv->readyVPQs[coreIdx]->startOfData );
   6.207 +      free( semEnv->readyVPQs[coreIdx] );
   6.208 +    }
   6.209 +   
   6.210 +   free( semEnv->readyVPQs );
   6.211 +
   6.212 +   
   6.213 +   //==== Free mutexes and mutex array ====
   6.214 +   mutexArray = semEnv->mutexDynArray->array;
   6.215 +   highestIdx = semEnv->mutexDynArray->highestIdxInArray;
   6.216 +   for( idx=0; idx < highestIdx; idx++ )
   6.217 +    { mutex = mutexArray[ idx ];
   6.218 +      if( mutex == NULL ) continue;
   6.219 +      free( mutex );
   6.220 +    }
   6.221 +   free( mutexArray );
   6.222 +   free( semEnv->mutexDynArray );
   6.223 +   //======================================
   6.224 +   
   6.225 +
   6.226 +   //==== Free conds and cond array ====
   6.227 +   condArray  = semEnv->condDynArray->array;
   6.228 +   highestIdx = semEnv->condDynArray->highestIdxInArray;
   6.229 +   for( idx=0; idx < highestIdx; idx++ )
   6.230 +    { cond = condArray[ idx ];
   6.231 +      if( cond == NULL ) continue;
   6.232 +      free( cond );
   6.233 +    }
   6.234 +   free( condArray );
   6.235 +   free( semEnv->condDynArray );
   6.236 +   //===================================
   6.237 +
   6.238 +   
   6.239 +   free( _VMSMasterEnv->semanticEnv );
   6.240 +   VMS__cleanup_after_shutdown();
   6.241 + }
   6.242 +
   6.243 +
   6.244 +//===========================================================================
   6.245 +
   6.246 +/*
   6.247 + */
   6.248 +VirtProcr *
   6.249 +VPThread__create_thread( VirtProcrFnPtr fnPtr, void *initData,
   6.250 +                          VirtProcr *animPr )
   6.251 + { VPThreadSemReq *reqData;
   6.252 +
   6.253 +   reqData = malloc( sizeof(VPThreadSemReq) );
   6.254 +   reqData->reqType      = make_procr;
   6.255 +   reqData->initData     = initData;
   6.256 +   reqData->fnPtr        = fnPtr;
   6.257 +   reqData->requestingPr = animPr;
   6.258 +
   6.259 +   VMS__add_sem_request( reqData, animPr );
   6.260 +   VMS__suspend_procr( animPr ); //will suspend then resume and continue
   6.261 +   return animPr->semanticData;  //result communicated back via semData field
   6.262 + }
   6.263 +
   6.264 +
   6.265 +inline void
   6.266 +VPThread__dissipate_thread( VirtProcr *procrToDissipate )
   6.267 + {
   6.268 +   VMS__dissipate_procr( procrToDissipate );
   6.269 + }
   6.270 +
   6.271 +
   6.272 +//===========================================================================
   6.273 +
   6.274 +void
   6.275 +VPThread__set_globals_to( void *globals )
   6.276 + {
   6.277 +   ((VPThreadSemEnv *)
   6.278 +    (_VMSMasterEnv->semanticEnv))->applicationGlobals = globals;
   6.279 + }
   6.280 +
   6.281 +void *
   6.282 +VPThread__give_globals()
   6.283 + {
   6.284 +   return((VPThreadSemEnv *)
   6.285 +          (_VMSMasterEnv->semanticEnv))->applicationGlobals;
   6.286 + }
   6.287 +
   6.288 +
   6.289 +
   6.290 +//===========================================================================
   6.291 +
   6.292 +int32
   6.293 +VPThread__make_mutex( VirtProcr *animPr )
   6.294 + { VPThreadSemReq *reqData;
   6.295 +
   6.296 +   reqData = malloc( sizeof(VPThreadSemReq) );
   6.297 +   reqData->reqType      = make_mutex;
   6.298 +   reqData->requestingPr = animPr;
   6.299 +
   6.300 +   VMS__add_sem_request( reqData, animPr );
   6.301 +   VMS__suspend_procr( animPr ); //will suspend then resume and continue
   6.302 +   return animPr->semanticData;  //result communicated back via semData field
   6.303 + }
   6.304 +
   6.305 +void
   6.306 +VPThread__mutex_lock( int32 mutexIdx, VirtProcr *acquiringPr )
   6.307 + { VPThreadSemReq *reqData;
   6.308 +
   6.309 +   reqData = malloc( sizeof(VPThreadSemReq) );
   6.310 +   reqData->reqType      = mutex_lock;
   6.311 +   reqData->mutexIdx     = mutexIdx;
   6.312 +   reqData->requestingPr = acquiringPr;
   6.313 +
   6.314 +   VMS__add_sem_request( reqData, acquiringPr );
   6.315 +   VMS__suspend_procr( acquiringPr ); //will resume when has the lock
   6.316 + }
   6.317 +
   6.318 +void
   6.319 +VPThread__mutex_unlock( int32 mutexIdx, VirtProcr *releasingPr )
   6.320 + { VPThreadSemReq *reqData;
   6.321 +
   6.322 +   reqData = malloc( sizeof(VPThreadSemReq) );
   6.323 +   reqData->reqType      = mutex_unlock;
   6.324 +   reqData->mutexIdx     = mutexIdx;
   6.325 +   reqData->requestingPr = releasingPr;
   6.326 +
   6.327 +   VMS__add_sem_request( reqData, releasingPr );
   6.328 +   VMS__suspend_procr( releasingPr ); //lock released when resumes
   6.329 + }
   6.330 +
   6.331 +
   6.332 +//=======================
   6.333 +int32
   6.334 +VPThread__make_cond( int32 ownedMutexIdx, VirtProcr *animPr)
   6.335 + { VPThreadSemReq *reqData;
   6.336 +
   6.337 +   reqData = malloc( sizeof(VPThreadSemReq) );
   6.338 +   reqData->reqType      = make_cond;
   6.339 +   reqData->mutexIdx     = ownedMutexIdx;
   6.340 +   reqData->requestingPr = animPr;
   6.341 +
   6.342 +   VMS__add_sem_request( reqData, animPr );
   6.343 +   VMS__suspend_procr( animPr ); //will suspend then resume and continue
   6.344 +   return animPr->semanticData;  //result communicated back via semData field
   6.345 + }
   6.346 +
   6.347 +void
   6.348 +VPThread__cond_wait( int32 condIdx, VirtProcr *waitingPr)
   6.349 + { VPThreadSemReq *reqData;
   6.350 +
   6.351 +   reqData = malloc( sizeof(VPThreadSemReq) );
   6.352 +   reqData->reqType      = cond_wait;
   6.353 +   reqData->condIdx      = condIdx;
   6.354 +   reqData->requestingPr = waitingPr;
   6.355 +
   6.356 +   VMS__add_sem_request( reqData, waitingPr );
   6.357 +   VMS__suspend_procr( waitingPr ); //resume when signalled & has lock
   6.358 + }
   6.359 +
   6.360 +void *
   6.361 +VPThread__cond_signal( int32 condIdx, VirtProcr *signallingPr )
   6.362 + { VPThreadSemReq *reqData;
   6.363 +
   6.364 +   reqData = malloc( sizeof(VPThreadSemReq) );
   6.365 +   reqData->reqType      = cond_signal;
   6.366 +   reqData->condIdx      = condIdx;
   6.367 +   reqData->requestingPr = signallingPr;
   6.368 +
   6.369 +   VMS__add_sem_request( reqData, signallingPr );
   6.370 +   VMS__suspend_procr( signallingPr );//resumes right away, still having lock
   6.371 + }
   6.372 +//===========================================================================
   6.373 +
   6.374 +/*Just thin wrapper for now -- semantic request is still a simple thing
   6.375 + * (July 3, 2010)
   6.376 + */
   6.377 +inline void
   6.378 +VPThread__free_semantic_request( VPThreadSemReq *semReq )
   6.379 + {
   6.380 +   free( semReq );
   6.381 + }
   6.382 +