/* **************************************************************** * * To compile do: * cc .c -o -lthread * **************************************************************** */ #include #include #include #include #include #include #include #include #define MAX_NUMP 12 /* Processor IDs are found, on a Sun SMP, through psrinfo */ int ProcessorIds[MAX_NUMP] = {0, 1, 4, 5, 6, 7, 8 , 9, 10, 11, 12, 13}; int NumProcs; mutex_t SyncLock; /* mutex */ cond_t SyncCV; /* condition variable */ int SyncCount; mutex_t ThreadLock; /* mutex */ hrtime_t StartTime; hrtime_t EndTime; int Count; /* Below is an implementation of a SpinLock. The idea is to use the * non-blocking mutex_trylock, which will try to attempt to get the mutex, * if it can't it will return with the EBUSY error. * * If the lock is busy, the inner while loop will monitor the lock by * trying to read a variable inside of the mutex_t structure, which tells * whether or not the mutex is locked. */ void SpinLock(mutex_t* mLock) { while(mutex_trylock(mLock) == EBUSY) { while(mLock->lock.lock64.pad[7]); } } /* Ad-hoc Barrier Code, this function will cause all the threads to synchronize * with each other. What happens is that the mutex lock is used to control * access to the condition variable & SyncCount variable. SyncCount is * initialized to 0 in the beginning, and when barrier is called by each * thread, SyncCount is incremented. When it reaches NumProcs, the * number of threads/processors, a conditional broadcast is sent out which * wakes up all the threads. The bad part is that each thread will then * contend over the mutex lock, SyncLock, and will be released sequentially. * * see man for further descriptions about cond_broadcast, cond_wait, etc. * * Created to be called only once.. things are re-initialized * * Barrier locks could also be implemented in many other ways, using * semaphores, and other sync. functions */ void Barrier() { int ret; SpinLock(&SyncLock); /* Get the thread lock */ SyncCount++; if(SyncCount == NumProcs) { ret = cond_broadcast(&SyncCV); assert(ret == 0); } else { ret = cond_wait(&SyncCV, &SyncLock); assert(ret == 0); } mutex_unlock(&SyncLock); } /* The function which is called once the thread is allocated */ void* ThreadLoop(void* tmp) { int threadId = (int) tmp; int ret; int startTime, endTime; /* ********************** Thread Initialization *********************** */ /* Bind the thread to a processor. This will make sure that each of * threads are on a different processor. ProcessorIds[threadId] * specifies the processor ID which the thread is binding to. */ ret = processor_bind(P_LWPID, P_MYID, ProcessorIds[threadId], NULL); assert(ret == 0); /* ********************** Thread Synchronization*********************** */ Barrier(); /* ********************** Execute Job ********************************* */ SpinLock(&ThreadLock); /* Get the thread lock */ Count++; mutex_unlock(&ThreadLock); /* Release the lock */ } main(int argc, char** argv) { thread_t* threads; int ret; int dx; /* fprintf(stderr, "%s \n", argv[0]); * assert(argc == (atoi(argv[1])+2)); * ProcessorIds = (int *) malloc(sizeof(int) * atoi(argv[1])); * assert(ProcessorIds != NULL); * for(dx=0; dx < NumProcs; dx++) { * ProcessorIds[dx] = atoi(argv[dx+2]); * } */ if(argc != 2) { fprintf(stderr, "%s \n", argv[0]); } assert(argc == 2); NumProcs = atoi(argv[1]); assert(NumProcs <= MAX_NUMP); /* Initialize array of thread structures */ threads = (thread_t *) malloc(sizeof(thread_t) * NumProcs); assert(threads != NULL); /* Initialize mutexs */ ret = mutex_init(&SyncLock, USYNC_THREAD, NULL); assert(ret == 0); ret = mutex_init(&ThreadLock, USYNC_THREAD, NULL); assert(ret == 0); /* Init condition variable */ ret = cond_init(&SyncCV, USYNC_THREAD, NULL); assert(ret == 0); SyncCount = 0; Count = 0; /* get high resolution timer, timer is expressed in nanoseconds, relative * to some arbitrary time.. so to get delta time must call gethrtime at * the end of operation and subtract the two times. */ StartTime = gethrtime(); for(dx=0; dx < NumProcs; dx++) { /* ************************************************************ * thr_create takes 6 parameters * p1: stack base, address of new stack, NULL means create new stack * p2: stack size, 0 uses the default stack size * p3: start routine, where new thread begins * p4: flags, THR_BOUND binds thread to a LWP (Light Weight Process) * p5: thread_t*, thread structure, could be NULL * ************************************************************ */ ret = thr_create(NULL, 0, ThreadLoop, (void *) dx, THR_BOUND, &threads[dx]); assert(ret == 0); } /* Wait for each of the threads to terminate */ for(dx=0; dx < NumProcs; dx++) { ret = thr_join(threads[dx], NULL, NULL); assert(ret == 0); } EndTime = gethrtime(); printf("Count = %d\n", Count); printf("Time = %lld nanoseconds\n", EndTime - StartTime); mutex_destroy(&ThreadLock); mutex_destroy(&SyncLock); cond_destroy(&SyncCV); }