/* threadhelper.c * * 7/23/2000 Sarah Anderson, LCSE * 2/20/2001 Merged win32/pthread+linux version - delete fortran binding * 3/28/2001 Added back fortran binding for t_create and abarrier and ifetchadd * * OS independent implementation of useful multithreading routines * including C and Fortran bindings. * */ #ifdef WIN32 #include #include #include #include #include // assert turned off by defining NDEBUG for windows #else #include #include #include #include #include #include #include #define DWORD int #define T_LOCK t_lock_ #define T_UNLOCK t_unlock_ #define T_INIT t_init_ #endif #define MXTHREADS 33 /* Maximum no. compute threads */ #define MXFADDLOCKS 65 #define debug 0 #include "threadhelper.h" /***************************************************************** * global data, all mutexes created with default attributes are fast * ones, i.e. can be locked only once before being unlocked *****************************************************************/ #define NSEM 10 /* one semaphore for ABARRIER(0), remainder for WAIT/RELEASE */ #define ABARRIERSEM 8 #define NCRIT 8 /* critical sections LOCK/UNLOCK */ /* interface to Linux/ia64 'fetch.c' atomic ops */ extern int fetch_and_add( int kounter, int increment ); extern void fetch_set( int kounter, int value ); extern int fetch_get( int kounter ); static unsigned long threads [ MXTHREADS ]; /* array of thread handles */ #ifndef WIN32 pthread_attr_t pthread_attributes; /* attributes object for threads */ pthread_mutex_t lock_bar; /* a lock for fbarrier */ pthread_mutex_t lock_zap; /* a lock for fbarrier */ pthread_mutex_t lock_bars; /* a lock for abarrier */ pthread_mutex_t lock_fadd; /* locks for ifetchadd */ pthread_mutex_t lock_crit[NCRIT]; /* lock for lock/unlock */ static int Semid; #include #if 0 static sem_t Psem; /* linux (posix) semaphore */ static sem_t Psem2; #endif #else HANDLE Semid[NSEM]; static CRITICAL_SECTION Csection; static CRITICAL_SECTION Critid[NCRIT]; #endif static int Nthreads=0; /* Number of spawned threads */ static unsigned int Stacksize= 0; /*---------------------------------------------------------------- */ long t_init( int node, unsigned int stacksize ) { int i; char name[80]; #ifndef WIN32 int istatus; ushort zeros[NSEM]; int semid_base; #endif Stacksize = stacksize; #ifdef WIN32 /* It seems NT is requiring me to name these */ for(i=0; i=0 && n=0 && n0; --i) sem_post(&Psem); */ fetch_set( 0,0 ); fetch_set(31,num); /* bars = 0;*/ sb.sem_op = nm1; /*...notify */ } else { /* sem_wait(&Psem); */ sb.sem_op = -1; /*... wait */ } if ( semop( Semid, &sb, 1 ) ) perror("swait"); while (! fetch_get(31) ) ; /* wait for last thread to set to num */ fetch_and_add( 31,-1 ); while (fetch_get(31)) ; /* wait for all threads to have decr. */ } void abarrier_(unsigned *num) { t_abarrier(*num); } #if 0 /*---------------------------------------------------------------- * A barrier which does not depend on pthread mutexs. * mythread is 1..num */ void t_bbarrier(unsigned mythread, unsigned int nthreads) { static volatile int btable[4]= {0,0,0,0}; int i,n; btable[mythread-1]= 1; do for(n=i=0; i0 && n < NSEM ); WaitForSingleObject( Semid[n], (DWORD)INFINITE ); } #else void t_wait( int n ) { int ierr; struct sembuf sb; assert( n>0 && n < NSEM ); /*fprintf(stderr,"##### t_wait %d\n", n ); */ sb.sem_num= n; sb.sem_op = -1; sb.sem_flg= 0; ierr= semop( Semid, &sb, 1 ); if ( ierr ) perror("t_wait"); } #endif /* **************************************************************** * Release threads. semnumber 0..NSEM-1, 0 reserved for barrier fun * ***************************************************************/ #ifdef WIN32 void t_release( int n ) { assert( n>0 && n < NSEM ); ReleaseSemaphore( Semid[n], 1, NULL ); } #else void t_release( int n ) { int ierr; struct sembuf sb; assert( n>0 && n < NSEM ); /*fprintf(stderr,"##### t_release %d\n", n ); */ sb.sem_num= n; sb.sem_op = 1; sb.sem_flg= 0; ierr= semop( Semid, &sb, 1 ); if ( ierr ) perror("t_release"); } #endif