diff --git a/main.cpp b/main.cpp index 2912aad..2883b45 100644 --- a/main.cpp +++ b/main.cpp @@ -3,17 +3,24 @@ * @author Blake North */ -#include // size_t -#include // memset -#include // cout +#include // size_t +#include // memset +#include +#include // cerr #include // vector // used for threads #include // pthread #include // sleep() - testing +#define THREAD_LOOP_COUNT 10 #define NUM_THREADS 8 +// 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); @@ -38,49 +45,70 @@ struct thread_data { void *thread_task_increment(const struct thread_data &thread) { // exists inside the loop - const size_t max_loops = 10; + SLEEP_HEAVY; + const size_t max_loops = THREAD_LOOP_COUNT; + + SLEEP_HEAVY; size_t loops = 0; - std::cout << "Hello from thread " << thread.id << "! (before run loop)" + SLEEP_HEAVY; + + std::cerr << "Hello from thread " << thread.id << "! (before run loop)" << std::endl; + SLEEP_HEAVY; + // thread loop, with an exit condition while (!(*thread.is_finished)) { + SLEEP_HEAVY; + // non-mutex operations { - std::cout << "Hello from thread " << thread.id + std::cerr << "Hello from thread " << thread.id << "! (inside run loop, before mutex)" << std::endl; } + SLEEP_HEAVY; + // mutex { // wait for permission to ask for the mutex while (!*thread.can_ask_for_mutex) - ; + SLEEP_LIGHT; + SLEEP_HEAVY; // say we want the mutex (*thread.wants_mutex) = true; + SLEEP_HEAVY; // block until we have the mutex while (!*thread.has_mutex) - ; + SLEEP_LIGHT; // enter the mutex - std::cout << "Hello from thread " << thread.id + std::cerr << "Hello from thread " << thread.id << "! (inside run loop, inside mutex)" << std::endl; + SLEEP_LIGHT; + + // increment the int (requires casting from void*) *static_cast(thread.data) += 1; + SLEEP_LIGHT; + // tell the thread manager we don't need the mutex (*thread.wants_mutex) = false; + SLEEP_HEAVY; // mutex exit } loops++; - if (max_loops < loops) + if (max_loops < loops) { + SLEEP_HEAVY; (*thread.is_finished) = true; + } } - std::cout << "Hello from thread " << thread.id << "! (done)" << std::endl; + std::cerr << "Hello from thread " << thread.id << "! (done)" << std::endl; pthread_exit(nullptr); } @@ -123,41 +151,54 @@ 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_HEAVY; pthread_create(&my_pthreads[tid], NULL, reinterpret_cast(thread_task_increment), &thread_data[tid]); + SLEEP_HEAVY; } - std::cout << "Threads have been spawned." << std::endl; + std::cerr << "Threads have been spawned." << std::endl; // loop until all threads are done for (size_t finished_threads = 0; finished_threads < threads.total_threads;) { + 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_LIGHT; if (threads.wants_mutex[tid_wants]) { // in case the mutex isn't used at all bool mutex_was_found = false; + 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_LIGHT; // we found who has the mutex! mutex_was_found = true; + SLEEP_LIGHT; // is the thread still using the mutex? if (!threads.wants_mutex[tid_has]) { + SLEEP_LIGHT; // take the mutex from the thread that has it threads.has_mutex[tid_has] = false; + SLEEP_LIGHT; threads.can_ask_for_mutex[tid_has] = true; + SLEEP_HEAVY; // give the mutex to the thread that wants it threads.can_ask_for_mutex[tid_wants] = false; + SLEEP_LIGHT; threads.has_mutex[tid_wants] = true; + SLEEP_HEAVY; } break; // no need to look at the rest if we found who has the mutex @@ -166,16 +207,22 @@ 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_LIGHT; threads.has_mutex[tid_wants] = true; + SLEEP_LIGHT; } + SLEEP_LIGHT; } } // find how many threads are done with the mutex finished_threads = 0; for (size_t tid = 0; tid < threads.total_threads; ++tid) { - if (threads.threads_finished[tid]) + if (threads.threads_finished[tid]) { finished_threads += 1; + SLEEP_LIGHT; + } + SLEEP_LIGHT; } } @@ -190,10 +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; + // 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; @@ -201,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); }