/* driver.c -- TA version -- TBD - test that the mutex and conditions are not global This code uses typecast between pointers and integral types, which is a usual, though not widely recommended, practice. Also, timeouts are supposed to be long enough for synchronization; this may need a closer look. */ #include #include #include #include #include "queue.h" #define time_elapsed( start, end ) \ (end.tv_sec - start.tv_sec) * 1000000L + \ (end.tv_usec - start.tv_usec) #define max2( a, b ) \ ((a) < (b) ? (b) : (a)) void out (char *txt) { if ( NULL == txt ) return; fprintf (stderr, "%s", txt ); } pthread_mutex_t me; pthread_cond_t cond; typedef struct { Queue *q, *qa; void *data; } param_t; /* test 7 */ /* tries to add an item to a full queue. */ /* param->data has the item to enqueue */ void* test7th1 (param_t *param) { enqueue (param->q, param->data); return (NULL); } /* waits for a while, then deletes an item */ void* test7th2 (param_t *param) { struct timespec delta, abstime; delta.tv_sec = 0; delta.tv_nsec = 1000; /* 1 usec */ delta.tv_nsec *= 1000; /* 1 msec */ delta.tv_nsec *= 10; /* 10 msec */ pthread_get_expiration_np (&delta, &abstime); pthread_mutex_lock (&me); pthread_cond_timedwait (&cond, &me, &abstime); pthread_mutex_unlock (&me); return dequeue (param->q, 0); } /* test 8, required */ /* enqueue numbers 1..param->data */ void *test8th1 (param_t *param) { long item, sum = 0; long i; for (i = 0; i < (long) param->data; ++i ) { item = i+1; sum += item; enqueue (param->q, (void*) item); } return ((void*)sum); } /* after a while, dequeue param->data items from the queue; / print them out as they are removed*/ void *test8th2 (param_t *param) { long sum = 0, item; long i; if ( 40 > (long) param->data ) printf ("\nDequeued:"); for ( i = 0; i < (long) param->data; i ++ ) { item = (long) dequeue (param->q, -1); if ( (long) (param->data) < 40 ) printf (" %ld", item ); sum += item; } if ( 40 > (long) param->data ) printf ("\n"); return ((void*)sum); } /* test 10 Consumer, producer, and prosumer */ /* Producer: enqueue param->data items to the queue; */ void *test10th1 (param_t *param) { long sum = 0, item; long i; for ( i = 0; i < (long) param->data; i ++ ) { item = i+1; enqueue (param->q, (void*) item); sum += item; } return ((void*)sum); } /* Consumer: dequeue param->data items from the queue; */ void *test10th2 (param_t *param) { long sum = 0, item; long i; for ( i = 0; i < (long) param->data; i ++ ) { item = (long) dequeue (param->q, -1); /* block */ sum += item; } return ((void*)sum); } /* Prosumer: enqueue a half of param->data items to the queue; then dequeue a half of para->data items. ASSUME param->data is even. */ void *test10th3 (param_t *param) { long balance = 0, item, offset; long i; offset = (long) param->data * ((long) param->data +1); offset /= 2; for ( i = 0; i < (long) param->data /2; ++i ) { item = offset + i+1; enqueue (param->q, (void*) item); balance += item; item = (long) dequeue (param->q, -1); /* block */ balance -= item; } return ((void*)balance); } /* test 11 Two queues */ /* Prosumer: suck off one queue, fill up the other one */ void *test11th1 (param_t *param) { long item; long balance = 0L; long i; for ( i = 0; i < (long) param->data; ++i ) { item = i +1; enqueue (param->q, (void*)item); balance += item; item = (long) dequeue (param->qa, -1); /* block */ balance -= item; } return ((void*)balance); } /* Prosumer: suck off one queue, fill up the other one */ void *test11th2 (param_t *param) { long item; long balance = 0L; long i; for ( i = 0; i < (long) param->data; ++i ) { item = (long) dequeue (param->q, -1); /* block */ balance -= item; item = i +1 + 100000L; enqueue (param->qa, (void*)item); balance += item; } return ((void*) balance); } /* .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. */ int test [] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; void select_tests (int c, char **v) { int i, t; int test_count; test_count = sizeof (test) / sizeof (*test); for (i = 1; i < c; ++i) { t = atoi (v [i]); if ( 0 == t && v [i][0] != '0' ) { fprintf (stderr, "***List tests NOT to perform\n"); exit (0); } test [abs (t)] = 0; } } main (int argc, char *argv[]) { int grade [] = { 5, /* 0) object storage */ 5, /* 1) probe on empty */ 5, /* 2) head on empty */ 5, /* 3) probe on non-empty */ 5, /* 4) head on non-empty */ 5, /* 5) timeout */ 5, /* 6) underflow */ 5, /* 7) overflow */ 15, /* 8) required test */ 5, /* 9) same, larger input */ 10, /* 10) producer/consumer/prosumer */ 10 /* 11) two queues, two prosumers */ /* 80 total. Add 10 points for maintaining local mutex and conditions per queue. Add 5 points for the correctness and completeness of submission. Add 5 points for clean compilation. Total 100. */ }; int total = 0; int maximum = 0; pthread_t th1, th2, th3; unsigned long u1 = 57, u2 = 179, u3, u4; void *v1, *v2, *v3, *v4, *v5; Queue *q = NULL, *qa = NULL; param_t param1, param2, param3; struct timeval tv_start, tv_end; unsigned long time_correction, time_eps; int test_id; select_tests (argc, argv); /* * One thread now: singular queue handling. */ out ("\n\t\t[Single threads]\n"); test_id = 0; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing object storage: ", test_id ); q = createQueue (15); v1 = &u1; enqueue (q, v1); v2 = dequeue (q, 0); if ( v1 == v2 ) { out ("ok\n"); total += grade [test_id]; }else { out ("ERROR\n"); } free (q); maximum += grade [test_id]; } test_id = 1; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing probe on empty queue: ", test_id ); q = createQueue (10); if ( ! probe (q) && ! probe (q) ) { /* make sure it releases mutex */ out ("ok\n"); total += grade [test_id]; }else { out ("ERROR"); } free (q); maximum += grade [test_id]; } test_id = 2; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing head on empty queue: ", test_id ); q = createQueue (5); if ( NULL == head (q) && NULL == head (q) ) { /* make sure it */ /* releases mutex */ out ("ok\n"); total += grade [test_id]; }else { out ("ERROR"); } free (q); maximum += grade [test_id]; } test_id = 3; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing probe on non-empty queue: ", test_id ); q = createQueue (10); enqueue (q, &u1); if ( probe (q) && probe (q) ) { /* make sure it releases mutex */ out ("ok\n"); total += grade [test_id]; }else { out ("ERROR"); } free (q); maximum += grade [test_id]; } test_id = 4; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing head on non-empty queue: ", test_id ); q = createQueue (10); v2 = &u2; enqueue (q, v2); if ( v2 == head (q) && v2 == head (q) ) { /* make sure it */ /* releases mutex and keeps head in queue */ out ("ok\n"); total += grade [test_id]; }else { out ("ERROR"); } free (q); maximum += grade [test_id]; } test_id = 6; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing underflow: ", test_id ); q = createQueue (2); enqueue (q, &u1); dequeue (q, 0); dequeue (q, 0); if ( NULL == head (q) && ! probe (q) ) { out ("ok\n"); total += grade [test_id]; }else { out ("ERROR\n"); } free (q); maximum += grade [test_id]; /* * Several threads. Producer/Consumer. */ out ("\n\t\t[Multiple threads]\n"); } test_id = 7; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing overflow: ", test_id ); q = createQueue (2); v1 = &u1; v2 = &u2; v3 = &u3; v4 = &u4; v5 = NULL; enqueue (q, v1); enqueue (q, v2); param2.q = param1.q = q; param1.data = v3; pthread_mutex_init (&me, NULL); pthread_cond_init (&cond, NULL); pthread_create (&th1, NULL, test7th1, ¶m1 ); pthread_create (&th2, NULL, test7th2, ¶m2 ); pthread_join (th1, &v4); pthread_join (th2, &v5); pthread_cond_destroy (&cond); pthread_mutex_destroy (&me); if ( v1 != head (q) && v3 != head (q) && v2 != head (q) ) { fprintf (stderr, "ERROR head=%#0x v1=%#0x v2=%#0x v3=%#0x v4=%#0x v5=%#0x", (unsigned) head (q), (unsigned) v1, (unsigned) v2, (unsigned) v3, (unsigned) v4, (unsigned) v5 ); }else if ( v2 != v5 && v1 != v5 ) { fprintf (stderr, "ERROR enqueue v1=%#0x v2=%#0x v3=%#0x v4=%#0x v5=%#0x\n", (unsigned) v1, (unsigned) v2, (unsigned) v3, (unsigned) v4, (unsigned) v5 ); }else { out ("ok\n"); total += grade [test_id]; } free (q); maximum += grade [test_id]; } test_id = 8; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d)Testing required: ", test_id ); q = createQueue (5); pthread_mutex_init (&me, NULL); pthread_cond_init (&cond, NULL); param2.q = param1.q = q; param2.data = param1.data = (void*) 10; pthread_create (&th1, NULL, test8th1, ¶m1 ); pthread_create (&th2, NULL, test8th2, ¶m2 ); pthread_join (th1, &v1); pthread_join (th2, &v2); fprintf (stderr, "Sum of items: enqueued %ld, dequeued %ld\n", (long) v1, (long) v2 ); pthread_cond_destroy (&cond); pthread_mutex_destroy (&me); if ( probe (q) || NULL != head (q) ) { out ("ERROR queue\n"); }else if ( (unsigned long) v1 != (unsigned long) v2 ) { out ("ERROR balance\n"); }else if ( 2 * (unsigned long) v1 != (unsigned long) param1.data * ( (unsigned long) param1.data +1) ) { fprintf (stderr, "ERROR sum=%lu, expected=%lu\n", (unsigned long) v1, (unsigned long) param1.data * ( (unsigned long) param1.data +1) /2 ); } else { out ("ok\n"); total += grade [test_id]; } free (q); maximum += grade [test_id]; } test_id = 9; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing required, only larger input.\n", test_id ); q = createQueue (5); pthread_mutex_init (&me, NULL); pthread_cond_init (&cond, NULL); param2.q = param1.q = q; param2.data = param1.data = (void*) 10000; pthread_create (&th1, NULL, test8th1, ¶m1 ); pthread_create (&th2, NULL, test8th2, ¶m2 ); pthread_join (th1, &v1); pthread_join (th2, &v2); fprintf (stderr, "Sum of items: enqueued %ld, dequeued %ld\n", (long) v1, (long) v2 ); pthread_cond_destroy (&cond); pthread_mutex_destroy (&me); if ( probe (q) || NULL != head (q) ) { out ("ERROR queue\n"); }else if ( (unsigned long) v1 != (unsigned long) v2 ) { out ("ERROR balance\n"); }else if ( 2 * (unsigned long) v1 != (unsigned long) param1.data * ( (unsigned long) param1.data +1) ) { fprintf (stderr, "ERROR sum=%lu, expected=%lu\n", (unsigned long) v1, (unsigned long) param1.data * ( (unsigned long) param1.data +1) /2 ); } else { out ("ok\n"); total += grade [test_id]; } free (q); maximum += grade [test_id]; } test_id = 10; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing producer, consumer, and prosumer: ", test_id); q = createQueue (10); pthread_mutex_init (&me, NULL); pthread_cond_init (&cond, NULL); param3.q = param2.q = param1.q = q; param3.data = param2.data = param1.data = (void*) 5000; /* even */ pthread_create (&th1, NULL, test10th1, ¶m1 ); pthread_create (&th2, NULL, test10th2, ¶m2 ); pthread_create (&th3, NULL, test10th3, ¶m3 ); pthread_join (th1, &v1); pthread_join (th2, &v2); pthread_join (th3, &v3); fprintf (stderr, "\nSum of items: producer %ld, consumer %ld, prosumer balance %ld\n", (long) v1, (long) v2, (long) v3 ); pthread_cond_destroy (&cond); pthread_mutex_destroy (&me); if ( probe (q) || NULL != head (q) ) { out ("ERROR queue\n"); }else if ( (long) v1 + (long) v3 != (long) v2 ) { out ("ERROR balance\n"); }else { out ("ok\n"); total += grade [test_id]; } free (q); maximum += grade [test_id]; } test_id = 11; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing two queues, two prosumers: ", test_id ); q = createQueue (10); qa = createQueue (20); pthread_mutex_init (&me, NULL); pthread_cond_init (&cond, NULL); param2.q = param1.q = q; param2.qa = param1.qa = qa; param1.data = param2.data = (void*) 2000; /* any number */ pthread_create (&th1, NULL, test11th1, ¶m1 ); pthread_create (&th2, NULL, test11th2, ¶m2 ); pthread_join (th1, &v1); pthread_join (th2, &v2); fprintf (stderr, "\nBalance of items: one %ld, another %ld\n", (long) v1, (long) v2 ); pthread_cond_destroy (&cond); pthread_mutex_destroy (&me); if ( probe (q) || probe (qa) || NULL != head (q) || NULL != head (qa) ) { out ("ERROR queue\n"); }else if ( (long) v1 + (long) v2 != 0 ) { out ("ERROR balance\n"); }else { out ("ok\n"); total += grade [test_id]; } free (q); free (qa); maximum += grade [test_id]; } test_id = 5; if ( test [test_id] ) { fprintf (stderr, "\n--\n%d) Testing timeout: ", test_id ); q = createQueue (2); if ( probe (q) ) dequeue (q, 1); if ( probe (q) ) dequeue (q, 1); if ( probe (q) ) dequeue (q, 1); u2 = 2000000; /* 2 sec */ gettimeofday (&tv_start, NULL); gettimeofday (&tv_end, NULL); time_correction = time_elapsed (tv_start, tv_end); gettimeofday (&tv_start, NULL); dequeue (q, u2); gettimeofday (&tv_end, NULL); u1 = time_elapsed (tv_start, tv_end) - time_correction; fprintf (stderr, "time elapsed %lu usec, expected %lu usec\n\t(correction %lu); ", u1, u2, time_correction ); time_eps = max2 (time_correction, 500000); /* up to 500 msec is okay */ if ( (unsigned long) labs (u1 - u2) < time_eps) { /* timeout discrepancy */ out ("ok\n"); total += grade [test_id]; }else { out ("TIMEOUT ASSUMED INCORRECT\n"); } free (q); maximum += grade [test_id]; } fprintf (stderr, "\n\n--\nTotal grade %d out of maximum %d\n", total, maximum ); }