From 4a2c82247e585cc894b68977143283a759bd6171 Mon Sep 17 00:00:00 2001 From: PowerUser64 Date: Tue, 19 Nov 2024 17:49:53 -0800 Subject: [PATCH 1/6] mutex: add random sleep intervals to attempt to cause problems --- main.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/main.cpp b/main.cpp index 2912aad..1874018 100644 --- a/main.cpp +++ b/main.cpp @@ -5,6 +5,7 @@ #include // size_t #include // memset +#include #include // cout #include // vector @@ -52,6 +53,8 @@ void *thread_task_increment(const struct thread_data &thread) { << "! (inside run loop, before mutex)" << std::endl; } + usleep(rand() % 20); + // mutex { @@ -60,6 +63,7 @@ void *thread_task_increment(const struct thread_data &thread) { ; // say we want the mutex (*thread.wants_mutex) = true; + usleep(rand() % 20); // block until we have the mutex while (!*thread.has_mutex) ; @@ -69,6 +73,8 @@ void *thread_task_increment(const struct thread_data &thread) { std::cout << "Hello from thread " << thread.id << "! (inside run loop, inside mutex)" << std::endl; + usleep(rand() % 20); + *static_cast(thread.data) += 1; // tell the thread manager we don't need the mutex @@ -123,9 +129,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) { + usleep(rand() % 20); pthread_create(&my_pthreads[tid], NULL, reinterpret_cast(thread_task_increment), &thread_data[tid]); + usleep(rand() % 20); } std::cout << "Threads have been spawned." << std::endl; @@ -133,14 +141,17 @@ void do_threading(struct thread_group threads) { // loop until all threads are done for (size_t finished_threads = 0; finished_threads < threads.total_threads;) { + usleep(rand() % 20); // 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) { + usleep(rand() % 20); if (threads.wants_mutex[tid_wants]) { // in case the mutex isn't used at all bool mutex_was_found = false; + usleep(rand() % 20); // 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) { @@ -148,16 +159,22 @@ void do_threading(struct thread_group threads) { if (threads.has_mutex[tid_has]) { // we found who has the mutex! mutex_was_found = true; + usleep(rand() % 20); // is the thread still using the mutex? if (!threads.wants_mutex[tid_has]) { + usleep(rand() % 20); // take the mutex from the thread that has it threads.has_mutex[tid_has] = false; + usleep(rand() % 20); threads.can_ask_for_mutex[tid_has] = true; + usleep(rand() % 20); // give the mutex to the thread that wants it threads.can_ask_for_mutex[tid_wants] = false; + usleep(rand() % 20); threads.has_mutex[tid_wants] = true; + usleep(rand() % 20); } break; // no need to look at the rest if we found who has the mutex @@ -166,8 +183,10 @@ 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) { + usleep(rand() % 20); threads.has_mutex[tid_wants] = true; } + usleep(rand() % 20); } } @@ -176,6 +195,7 @@ 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; + usleep(rand() % 20); } } @@ -190,6 +210,8 @@ void do_threading(struct thread_group threads) { int main(void) { struct thread_group mythreads; + srand(time(NULL)); + // the count int count = 0; From 1043680b195f0ba03f893a2aa9ebce8d0d7bf0c2 Mon Sep 17 00:00:00 2001 From: PowerUser64 Date: Tue, 19 Nov 2024 17:52:18 -0800 Subject: [PATCH 2/6] mutex: add random sleep intervals to test stability --- main.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/main.cpp b/main.cpp index 1874018..cb765cf 100644 --- a/main.cpp +++ b/main.cpp @@ -53,7 +53,7 @@ void *thread_task_increment(const struct thread_data &thread) { << "! (inside run loop, before mutex)" << std::endl; } - usleep(rand() % 20); + usleep(rand() % 200); // mutex { @@ -63,7 +63,7 @@ void *thread_task_increment(const struct thread_data &thread) { ; // say we want the mutex (*thread.wants_mutex) = true; - usleep(rand() % 20); + usleep(rand() % 200); // block until we have the mutex while (!*thread.has_mutex) ; @@ -73,7 +73,7 @@ void *thread_task_increment(const struct thread_data &thread) { std::cout << "Hello from thread " << thread.id << "! (inside run loop, inside mutex)" << std::endl; - usleep(rand() % 20); + usleep(rand() % 200); *static_cast(thread.data) += 1; @@ -129,11 +129,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) { - usleep(rand() % 20); + usleep(rand() % 200); pthread_create(&my_pthreads[tid], NULL, reinterpret_cast(thread_task_increment), &thread_data[tid]); - usleep(rand() % 20); + usleep(rand() % 200); } std::cout << "Threads have been spawned." << std::endl; @@ -141,17 +141,17 @@ void do_threading(struct thread_group threads) { // loop until all threads are done for (size_t finished_threads = 0; finished_threads < threads.total_threads;) { - usleep(rand() % 20); + usleep(rand() % 200); // 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) { - usleep(rand() % 20); + usleep(rand() % 200); if (threads.wants_mutex[tid_wants]) { // in case the mutex isn't used at all bool mutex_was_found = false; - usleep(rand() % 20); + usleep(rand() % 200); // 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) { @@ -159,22 +159,22 @@ void do_threading(struct thread_group threads) { if (threads.has_mutex[tid_has]) { // we found who has the mutex! mutex_was_found = true; - usleep(rand() % 20); + usleep(rand() % 200); // is the thread still using the mutex? if (!threads.wants_mutex[tid_has]) { - usleep(rand() % 20); + usleep(rand() % 200); // take the mutex from the thread that has it threads.has_mutex[tid_has] = false; - usleep(rand() % 20); + usleep(rand() % 200); threads.can_ask_for_mutex[tid_has] = true; - usleep(rand() % 20); + usleep(rand() % 200); // give the mutex to the thread that wants it threads.can_ask_for_mutex[tid_wants] = false; - usleep(rand() % 20); + usleep(rand() % 200); threads.has_mutex[tid_wants] = true; - usleep(rand() % 20); + usleep(rand() % 200); } break; // no need to look at the rest if we found who has the mutex @@ -183,10 +183,10 @@ 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) { - usleep(rand() % 20); + usleep(rand() % 200); threads.has_mutex[tid_wants] = true; } - usleep(rand() % 20); + usleep(rand() % 200); } } @@ -195,7 +195,7 @@ 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; - usleep(rand() % 20); + usleep(rand() % 200); } } From d9f76c137295aaa277a591623a09e92ad7f1df65 Mon Sep 17 00:00:00 2001 From: PowerUser64 Date: Tue, 19 Nov 2024 17:52:49 -0800 Subject: [PATCH 3/6] test-runner: increase test count to 10000, improve output --- test-sourceme.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test-sourceme.sh b/test-sourceme.sh index 4a2da65..ebb1f2b 100644 --- a/test-sourceme.sh +++ b/test-sourceme.sh @@ -2,7 +2,7 @@ # DO NOT RUN THIS, SOURCE IT FROM YOUR TERMINAL (idk why it needs this) -for ((i=0; i < 1000; ++i)); do +for ((i=0; i < 10000; ++i)); do if timeout -s INT 0.8 ./mutex.out > /dev/null # if grep -q 'Post: count: 99'; then # : # good result @@ -19,6 +19,9 @@ for ((i=0; i < 1000; ++i)); do if ((i % 100 == 99)); then echo -n . fi + if ((i % 1000 == 999)); then + echo " $((i + 1))" + fi else echo -n "-ERROR on take ${i}: $e-" e= From 160d22ded850b4eecf792573d4e12a3ee8c25dd9 Mon Sep 17 00:00:00 2001 From: PowerUser64 Date: Mon, 25 Nov 2024 22:41:22 -0800 Subject: [PATCH 4/6] mutex: more random sleeps --- main.cpp | 74 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/main.cpp b/main.cpp index cb765cf..d17dac0 100644 --- a/main.cpp +++ b/main.cpp @@ -3,8 +3,8 @@ * @author Blake North */ -#include // size_t -#include // memset +#include // size_t +#include // memset #include #include // cout #include // vector @@ -13,8 +13,11 @@ #include // pthread #include // sleep() - testing +#define THREAD_LOOP_COUNT 10 #define NUM_THREADS 8 +#define SLEEP usleep(rand() % 50) // random sleep delay + typedef void *(*ThreadTask)(const struct thread_data &); typedef void *(*PthreadFun)(void *arg); @@ -39,51 +42,67 @@ struct thread_data { void *thread_task_increment(const struct thread_data &thread) { // exists inside the loop - const size_t max_loops = 10; + SLEEP; + const size_t max_loops = THREAD_LOOP_COUNT; + + SLEEP; size_t loops = 0; + SLEEP; + std::cout << "Hello from thread " << thread.id << "! (before run loop)" << std::endl; + SLEEP; + // thread loop, with an exit condition while (!(*thread.is_finished)) { + SLEEP; + // non-mutex operations { std::cout << "Hello from thread " << thread.id << "! (inside run loop, before mutex)" << std::endl; } - usleep(rand() % 200); + SLEEP; // mutex { // wait for permission to ask for the mutex while (!*thread.can_ask_for_mutex) - ; + SLEEP; + SLEEP; // say we want the mutex (*thread.wants_mutex) = true; - usleep(rand() % 200); + SLEEP; // block until we have the mutex while (!*thread.has_mutex) - ; + SLEEP; // enter the mutex std::cout << "Hello from thread " << thread.id << "! (inside run loop, inside mutex)" << std::endl; - usleep(rand() % 200); + SLEEP; + // increment the int (requires casting from void*) *static_cast(thread.data) += 1; + SLEEP; + // tell the thread manager we don't need the mutex (*thread.wants_mutex) = false; + SLEEP; // mutex exit } loops++; - if (max_loops < loops) + if (max_loops < loops) { + SLEEP; (*thread.is_finished) = true; + } } std::cout << "Hello from thread " << thread.id << "! (done)" << std::endl; @@ -129,11 +148,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) { - usleep(rand() % 200); + SLEEP; pthread_create(&my_pthreads[tid], NULL, reinterpret_cast(thread_task_increment), &thread_data[tid]); - usleep(rand() % 200); + SLEEP; } std::cout << "Threads have been spawned." << std::endl; @@ -141,40 +160,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;) { - usleep(rand() % 200); + SLEEP; // 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) { - usleep(rand() % 200); + SLEEP; if (threads.wants_mutex[tid_wants]) { // in case the mutex isn't used at all bool mutex_was_found = false; - usleep(rand() % 200); + SLEEP; // 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; // we found who has the mutex! mutex_was_found = true; - usleep(rand() % 200); + SLEEP; // is the thread still using the mutex? if (!threads.wants_mutex[tid_has]) { - usleep(rand() % 200); + SLEEP; // take the mutex from the thread that has it threads.has_mutex[tid_has] = false; - usleep(rand() % 200); + SLEEP; threads.can_ask_for_mutex[tid_has] = true; - usleep(rand() % 200); + SLEEP; // give the mutex to the thread that wants it threads.can_ask_for_mutex[tid_wants] = false; - usleep(rand() % 200); + SLEEP; threads.has_mutex[tid_wants] = true; - usleep(rand() % 200); + SLEEP; } break; // no need to look at the rest if we found who has the mutex @@ -183,19 +204,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) { - usleep(rand() % 200); + SLEEP; threads.has_mutex[tid_wants] = true; + SLEEP; } - usleep(rand() % 200); + SLEEP; } } // 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; - usleep(rand() % 200); + SLEEP; + } + SLEEP; } } @@ -216,6 +240,8 @@ int main(void) { int count = 0; std::cout << "Pre: " << DBG_PRINT(count) << std::endl; + std::cout << "Expected: " << NUM_THREADS * (THREAD_LOOP_COUNT + 1) + count + << std::endl; mythreads.data = &count; mythreads.total_threads = NUM_THREADS; From 22a12e2995b1c59161214d26da631af26c4ea1c1 Mon Sep 17 00:00:00 2001 From: PowerUser64 Date: Mon, 25 Nov 2024 22:42:17 -0800 Subject: [PATCH 5/6] mutex: use cerr for printing debug info --- main.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main.cpp b/main.cpp index d17dac0..a1e1ca0 100644 --- a/main.cpp +++ b/main.cpp @@ -6,7 +6,7 @@ #include // size_t #include // memset #include -#include // cout +#include // cerr #include // vector // used for threads @@ -50,7 +50,7 @@ void *thread_task_increment(const struct thread_data &thread) { SLEEP; - std::cout << "Hello from thread " << thread.id << "! (before run loop)" + std::cerr << "Hello from thread " << thread.id << "! (before run loop)" << std::endl; SLEEP; @@ -61,7 +61,7 @@ void *thread_task_increment(const struct thread_data &thread) { // non-mutex operations { - std::cout << "Hello from thread " << thread.id + std::cerr << "Hello from thread " << thread.id << "! (inside run loop, before mutex)" << std::endl; } @@ -83,7 +83,7 @@ void *thread_task_increment(const struct thread_data &thread) { // 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; @@ -105,7 +105,7 @@ void *thread_task_increment(const struct thread_data &thread) { } } - std::cout << "Hello from thread " << thread.id << "! (done)" << std::endl; + std::cerr << "Hello from thread " << thread.id << "! (done)" << std::endl; pthread_exit(nullptr); } @@ -155,7 +155,7 @@ void do_threading(struct thread_group threads) { SLEEP; } - 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;) { From 808c511a7b3111684ea4f6ff28fb57787b2c51f5 Mon Sep 17 00:00:00 2001 From: PowerUser64 Date: Tue, 26 Nov 2024 00:14:58 -0800 Subject: [PATCH 6/6] mutex: make light and heavy sleep --- main.cpp | 79 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 36 deletions(-) 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); }