/* Test program for cs6420 program 1 "GThreads" */ #include #include #include #include "gthreads.h" #define N_THREADS 10 #define N_PASSES 1000 #define T2_COUNT 1000000 #define T3_YIELDS 10 int Done[N_THREADS]; /* TRUE for each thread that is done */ int Failed[N_THREADS]; /* TRUE for each thread that failed */ int PassCount[N_THREADS]; /* Each thread keeps up with passes */ long* T2Log[N_THREADS]; /* Area to log t2 results */ GT_MUTEX T3Mutex; /* Mutex for test 3 */ int T3Count; /* Count for test 3 */ GT_MUTEX T4Mutex; /* Mutex for test 4 */ /* Thread for test1, testing GThread_yield() and stack check */ /* Insures that after each GTHread_yield, all other threads */ /* have scheduled. Verifies each thread gets a private stack */ void ThreadT1( void* pParam) { #define VERIFY_SIZE 35000 int MyId; int i, j; int StackVerify[VERIFY_SIZE]; int OtherCount; int NewOtherCount; int Others; int OldPassCount[N_THREADS]; MyId = (int)pParam; PassCount[MyId] = 0; for (j = 0; j < VERIFY_SIZE; j++) StackVerify[j] = MyId; printf("Thread %d starting\n", MyId); /*printf("Thr %d Init stack at %p\n", MyId, StackVerify);*/ for (i = 0; i < N_PASSES; i++) { PassCount[MyId]++; /* Add up pass count of others before yield */ OtherCount = 0; for (j = 0; j < N_THREADS; j++) { if (j != MyId) OtherCount += PassCount[j]; OldPassCount[j] = PassCount[j]; } /* Now yield to allow other thread to execute */ GThread_yield(); /* Allow some other thread to use CPU */ /* Add up pass count of others after yield */ NewOtherCount = 0; Others = 0; for (j = 0; j < N_THREADS; j++) { if (j != MyId) { if (!Done[j]) { /* Insure count goes up for uncompleted threads */ if (PassCount[j] <= OldPassCount[j]) { Failed[MyId] = 1; /* Failed, all others should increase */ Done[MyId] = 1; printf("Thread %d exiting with error 1\n", MyId); GThread_exit(); } } if (!Done[j]) Others++; NewOtherCount += PassCount[j]; } } if (OtherCount == NewOtherCount && Others != 0) { /* Error, yield did not yield */ printf("Thread %d Yield Failed! OtherCount %d NewOtherCount %d\n", MyId, OtherCount, NewOtherCount); /* Go ahead and exit, but set failure */ Failed[MyId] = 1; Done[MyId] = 1; /* Set we are done */ printf("Thread %d exiting with error 2\n", MyId); GThread_exit(); } } /* Verify stack still intact */ printf("Thr %d Verifying stack at %p\n", MyId, StackVerify); for (j = 0; j < VERIFY_SIZE; j++) { if (StackVerify[j] != MyId) { printf("Stack verification error, thread %d\n", MyId); exit(0); } } Done[MyId] = 1; /* Set we are done */ printf("Thread %d exiting\n", MyId); GThread_exit(); } /* Thread for test2, testing pre-emptive multitasking */ /* Insures that after thread counts to 1,000,000 without yielding */ /* All other threads have at least counted some also */ /* Obviously this requires that it take longer than "SchedulingInterval" */ /* To count to 1,000,000. Values were empirically determined on system */ /* "elvis" to be reasonable */ void ThreadT2( void* pParam) { int MyId; int i; double d, e, f, g, h; long* pT2Log; MyId = (int)pParam; PassCount[MyId] = 0; printf("Thread %d starting\n", MyId); e = 0.0; f = 0.0; g = 0.0; h = 0.0; for (i = 0; i < T2_COUNT; i++) { PassCount[MyId]++; /* Burn a little cpu time */ d = sqrt(e * f + g * h); e += 1.0; f += 2.0; g += 3.0; h += 4.0; } /* Verify all other threads have at least counted a little bit */ for (i = 0; i < N_THREADS; i++) { if (PassCount[i] == 0) { /* Thread did not preempt properly */ Failed[MyId] = 1; Done[MyId] = 1; /* Set we are done */ printf("Thread %d exiting with error 1\n", MyId); GThread_exit(); } } pT2Log = T2Log[MyId]; /* Get pointer to log area */ for (i = 0; i < N_THREADS; i++) pT2Log[i] = PassCount[i]; /* Save pass count */ printf("Thread %d exiting\n", MyId); GThread_exit(); } /* Thread for test3, mutex locking */ /* Locks a mutex, and increments a variable. Other threads try to lock */ /* Same mutex and increment same variable. Variable should never increment */ /* to greater than 1 */ void ThreadT3( void* pParam) { int MyId; int i, j; MyId = (int)pParam; printf("Thread %d starting\n", MyId); for (i = 0; i < N_PASSES; i++) { PassCount[MyId]++; GTMutex_lock(T3Mutex); /* Lock the mutex */ T3Count++; if (T3Count > 1) { /* Failed! mutex did not lock properly */ Failed[MyId] = 1; Done[MyId] = 1; printf("Thread %d exiting with error 1\n", MyId); GThread_exit(); } for (j = 0; j < T3_YIELDS; j++) GThread_yield(); T3Count--; if (T3Count < 0) { /* Failed! mutex did not lock properly */ Failed[MyId] = 1; Done[MyId] = 1; printf("Thread %d exiting with error 2\n", MyId); GThread_exit(); } GTMutex_unlock(T3Mutex); GThread_yield(); } printf("Thread %d exiting\n", MyId); GThread_exit(); } /* Thread for test4, mutex owner unlocking */ /* Thread 0 locks a mutex and waits for all other threads to complete */ /* Other threads wait for it to go locked, they try to unlock it. Since */ /* they are not the owner, the unlock should fail and the lock should */ /* remain locked */ void ThreadT4( void* pParam) { int MyId; MyId = (int)pParam; printf("Thread %d starting\n", MyId); if (MyId == 0) { GTMutex_lock(T4Mutex); /* Lock the mutex */ while (GThread_count() > 2) GThread_yield(); if (!GTMutex_islocked(T4Mutex)) { Failed[MyId] = 1; /* Should still be locked */ printf("Thread %d exiting with error 1\n", MyId); } if (GTMutex_unlock(T4Mutex) != GT_SUCCESS) { Failed[MyId] = 1; /* Unlock failed */ printf("Thread %d exiting with error 2\n", MyId); } } else { /* Not thread 0, wait for lock to set */ while(!GTMutex_islocked(T4Mutex)) GThread_yield(); /* Wait for thread 0 */ if (GTMutex_unlock(T4Mutex) == GT_SUCCESS) { Failed[MyId] = 1; /* Should have failed, not owner */ printf("Thread %d exiting with error 3\n", MyId); } if (!GTMutex_islocked(T4Mutex)) { Failed[MyId] = 1; /* Should still be locked */ printf("Thread %d exiting with error 4\n", MyId); } } printf("Thread %d exiting\n", MyId); GThread_exit(); } /* Main programs for each test */ int Test1() { int T1Failed = 0; int i; GThread_init(0); printf("Running test 1, GThread_yield()\n"); for (i = 0; i < N_THREADS; i++) { /* Create each thread */ if (GThread_create(ThreadT1, (void*)i, 40000) != GT_SUCCESS) { printf("Error creating thread %d\n", i); exit(1); } } while((i = GThread_count()) > 1) GThread_yield(); /* Verify every thread exited without error */ for (i = 0; i < N_THREADS; i++) { if (PassCount[i] == 0) T1Failed = 1; /* Thread never scheduled */ if (Failed[i]) T1Failed = 1; } if (T1Failed) printf("Test 1 Failed\n"); else printf("Test 1 passed\n"); return(0); } int Test2() { int i, j; int T2Failed = 0; long* pT2Log; /* Set up log area */ for (i = 0; i < N_THREADS; i++) { T2Log[i] = (long*)malloc(sizeof(long)*N_THREADS); } GThread_init(100000); printf("Running test 2, Pre-emptive multitasking\n"); for (i = 0; i < N_THREADS; i++) { /* Create each thread */ if (GThread_create(ThreadT2, (void*)i, 10000) != GT_SUCCESS) { printf("Error creating thread %d\n", i); exit(1); } } while((i = GThread_count()) > 1) { } /* Just spin...*/ /* Verify every thread exited without error */ for (i = 0; i < N_THREADS; i++) { pT2Log = T2Log[i]; /* Get pointer to log area */ printf("Thr %d - ", i); for (j = 0; j < N_THREADS; j++) printf("%8ld", pT2Log[j]); printf("\n"); if (PassCount[i] == 0) T2Failed = 1; /* Thread never scheduled */ if (Failed[i]) T2Failed = 1; } if (T2Failed) printf("Test 2 Failed\n"); else printf("Test 2 passed\n"); return(0); } int Test3() { int T3Failed = 0; int i; GThread_init(0); /* Yielding only, no pre-empting*/ GTMutex_create(&T3Mutex); /* Create the mutex for test3 */ T3Count = 0; printf("Running test 3, Mutex locking and thread scheduling\n"); for (i = 0; i < N_THREADS; i++) { /* Create each thread */ if (GThread_create(ThreadT3, (void*)i, 10000) != GT_SUCCESS) { printf("Error creating thread %d\n", i); exit(1); } } while((i = GThread_count()) > 1) GThread_yield(); /* Verify every thread exited without error */ for (i = 0; i < N_THREADS; i++) { if (PassCount[i] == 0) T3Failed = 1; /* Thread never scheduled */ if (Failed[i]) T3Failed = 1; } printf("Successful lock count %d, unsuccessful lock count %d\n", GTMutex_SCount(T3Mutex), GTMutex_UCount(T3Mutex)); printf("SCount should be 10000, UCount should be about the same but not greater\n"); if (GTMutex_UCount(T3Mutex) > GTMutex_SCount(T3Mutex)) T3Failed = 1; if (T3Failed) printf("Test 3 Failed\n"); else printf("Test 3 passed\n"); return(0); } int Test4() { int T4Failed = 0; int i; GThread_init(0); /* Non-preempting*/ GTMutex_create(&T4Mutex); /* Create the mutex for test4 */ printf("Running test 4, Mutex owner unlocking\n"); for (i = 0; i < N_THREADS; i++) { /* Create each thread */ if (GThread_create(ThreadT4, (void*)i, 10000) != GT_SUCCESS) { printf("Error creating thread %d\n", i); exit(1); } } while( GThread_count() > 1) GThread_yield(); /* Verify every thread exited without error */ for (i = 0; i < N_THREADS; i++) { if (Failed[i]) T4Failed = 1; } if (T4Failed) printf("Test 4 Failed\n"); else printf("Test 4 passed\n"); return(0); } int main( int argc, char** argv) { long Testnum; if (argc != 2) { printf("Usage: testgt testnum\n"); exit(1); } Testnum = atol(argv[1]); if (Testnum <= 0 || Testnum > 4) { printf("Test number (%s) out of range\n", argv[1]); exit(2); } switch(Testnum) { case 1: Test1(); break; case 2: Test2(); break; case 3: Test3(); break; case 4: Test4(); break; } return(0); }