diff --git a/main.cpp b/main.cpp index a1e1ca0..2883b45 100644 --- a/main.cpp +++ b/main.cpp @@ -16,7 +16,10 @@ #define THREAD_LOOP_COUNT 10 #define NUM_THREADS 8 -#define SLEEP usleep(rand() % 50) // random sleep delay +// different lengths of sleep, so the test can run in a reasonable time +#define RANDSLEEP(c) usleep(rand() % c) // random sleep delay +#define SLEEP_LIGHT /* RANDSLEEP(10) */ // short sleep, for blocking code +#define SLEEP_HEAVY RANDSLEEP(100) // long sleep, for non-blocking code typedef void *(*ThreadTask)(const struct thread_data &); typedef void *(*PthreadFun)(void *arg); @@ -42,22 +45,22 @@ struct thread_data { void *thread_task_increment(const struct thread_data &thread) { // exists inside the loop - SLEEP; + SLEEP_HEAVY; const size_t max_loops = THREAD_LOOP_COUNT; - SLEEP; + SLEEP_HEAVY; size_t loops = 0; - SLEEP; + SLEEP_HEAVY; std::cerr << "Hello from thread " << thread.id << "! (before run loop)" << std::endl; - SLEEP; + SLEEP_HEAVY; // thread loop, with an exit condition while (!(*thread.is_finished)) { - SLEEP; + SLEEP_HEAVY; // non-mutex operations { @@ -65,42 +68,42 @@ void *thread_task_increment(const struct thread_data &thread) { << "! (inside run loop, before mutex)" << std::endl; } - SLEEP; + SLEEP_HEAVY; // mutex { // wait for permission to ask for the mutex while (!*thread.can_ask_for_mutex) - SLEEP; - SLEEP; + SLEEP_LIGHT; + SLEEP_HEAVY; // say we want the mutex (*thread.wants_mutex) = true; - SLEEP; + SLEEP_HEAVY; // block until we have the mutex while (!*thread.has_mutex) - SLEEP; + SLEEP_LIGHT; // enter the mutex std::cerr << "Hello from thread " << thread.id << "! (inside run loop, inside mutex)" << std::endl; - SLEEP; + SLEEP_LIGHT; // increment the int (requires casting from void*) *static_cast(thread.data) += 1; - SLEEP; + SLEEP_LIGHT; // tell the thread manager we don't need the mutex (*thread.wants_mutex) = false; - SLEEP; + SLEEP_HEAVY; // mutex exit } loops++; if (max_loops < loops) { - SLEEP; + SLEEP_HEAVY; (*thread.is_finished) = true; } } @@ -148,11 +151,11 @@ void do_threading(struct thread_group threads) { // spawn threads (none will enter the mutex yet) for (size_t tid = 0; tid < threads.total_threads; ++tid) { - SLEEP; + SLEEP_HEAVY; pthread_create(&my_pthreads[tid], NULL, reinterpret_cast(thread_task_increment), &thread_data[tid]); - SLEEP; + SLEEP_HEAVY; } std::cerr << "Threads have been spawned." << std::endl; @@ -160,42 +163,42 @@ void do_threading(struct thread_group threads) { // loop until all threads are done for (size_t finished_threads = 0; finished_threads < threads.total_threads;) { - SLEEP; + SLEEP_LIGHT; // TODO: make sure we cycle the mutex through threads round-robin style // hand off the mutex to threads that want it for (size_t tid_wants = 0; tid_wants < threads.total_threads; ++tid_wants) { - SLEEP; + SLEEP_LIGHT; if (threads.wants_mutex[tid_wants]) { // in case the mutex isn't used at all bool mutex_was_found = false; - SLEEP; + SLEEP_LIGHT; // find which thread has the mutex and hand it off if it's done for (size_t tid_has = 0; tid_has < threads.total_threads; ++tid_has) { if (threads.has_mutex[tid_has]) { - SLEEP; + SLEEP_LIGHT; // we found who has the mutex! mutex_was_found = true; - SLEEP; + SLEEP_LIGHT; // is the thread still using the mutex? if (!threads.wants_mutex[tid_has]) { - SLEEP; + SLEEP_LIGHT; // take the mutex from the thread that has it threads.has_mutex[tid_has] = false; - SLEEP; + SLEEP_LIGHT; threads.can_ask_for_mutex[tid_has] = true; - SLEEP; + SLEEP_HEAVY; // give the mutex to the thread that wants it threads.can_ask_for_mutex[tid_wants] = false; - SLEEP; + SLEEP_LIGHT; threads.has_mutex[tid_wants] = true; - SLEEP; + SLEEP_HEAVY; } break; // no need to look at the rest if we found who has the mutex @@ -204,11 +207,11 @@ void do_threading(struct thread_group threads) { // give the thread the mutex if it wasn't found to be in use if (!mutex_was_found) { - SLEEP; + SLEEP_LIGHT; threads.has_mutex[tid_wants] = true; - SLEEP; + SLEEP_LIGHT; } - SLEEP; + SLEEP_LIGHT; } } @@ -217,9 +220,9 @@ void do_threading(struct thread_group threads) { for (size_t tid = 0; tid < threads.total_threads; ++tid) { if (threads.threads_finished[tid]) { finished_threads += 1; - SLEEP; + SLEEP_LIGHT; } - SLEEP; + SLEEP_LIGHT; } } @@ -234,14 +237,16 @@ void do_threading(struct thread_group threads) { int main(void) { struct thread_group mythreads; + // initialize random (for random sleep amounts) srand(time(NULL)); // the count int count = 0; - std::cout << "Pre: " << DBG_PRINT(count) << std::endl; - std::cout << "Expected: " << NUM_THREADS * (THREAD_LOOP_COUNT + 1) + count - << std::endl; + // expected result + const int count_expected = NUM_THREADS * (THREAD_LOOP_COUNT + 1) + count; + + std::cout << "Expected: " << count_expected << std::endl; mythreads.data = &count; mythreads.total_threads = NUM_THREADS; @@ -249,5 +254,7 @@ int main(void) { do_threading(mythreads); - std::cout << "Post: " << DBG_PRINT(count) << std::endl; + std::cout << "Got: " << count << std::endl; + + return !(count_expected == count); }