Compare commits

..

1 commit

2 changed files with 19 additions and 71 deletions

View file

@ -3,24 +3,17 @@
* @author Blake North <blake.north@digipen.edu> * @author Blake North <blake.north@digipen.edu>
*/ */
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstring> // memset #include <cstring> // memset
#include <ctime> #include <iostream> // cout
#include <iostream> // cerr
#include <vector> // vector #include <vector> // vector
// used for threads // used for threads
#include <pthread.h> // pthread #include <pthread.h> // pthread
#include <unistd.h> // sleep() - testing #include <unistd.h> // sleep() - testing
#define THREAD_LOOP_COUNT 10
#define NUM_THREADS 8 #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 *(*ThreadTask)(const struct thread_data &);
typedef void *(*PthreadFun)(void *arg); typedef void *(*PthreadFun)(void *arg);
@ -45,70 +38,49 @@ struct thread_data {
void *thread_task_increment(const struct thread_data &thread) { void *thread_task_increment(const struct thread_data &thread) {
// exists inside the loop // exists inside the loop
SLEEP_HEAVY; const size_t max_loops = 10;
const size_t max_loops = THREAD_LOOP_COUNT;
SLEEP_HEAVY;
size_t loops = 0; size_t loops = 0;
SLEEP_HEAVY; std::cout << "Hello from thread " << thread.id << "! (before run loop)"
std::cerr << "Hello from thread " << thread.id << "! (before run loop)"
<< std::endl; << std::endl;
SLEEP_HEAVY;
// thread loop, with an exit condition // thread loop, with an exit condition
while (!(*thread.is_finished)) { while (!(*thread.is_finished)) {
SLEEP_HEAVY;
// non-mutex operations // non-mutex operations
{ {
std::cerr << "Hello from thread " << thread.id std::cout << "Hello from thread " << thread.id
<< "! (inside run loop, before mutex)" << std::endl; << "! (inside run loop, before mutex)" << std::endl;
} }
SLEEP_HEAVY;
// mutex // mutex
{ {
// wait for permission to ask for the mutex // wait for permission to ask for the mutex
while (!*thread.can_ask_for_mutex) while (!*thread.can_ask_for_mutex)
SLEEP_LIGHT; ;
SLEEP_HEAVY;
// say we want the mutex // say we want the mutex
(*thread.wants_mutex) = true; (*thread.wants_mutex) = true;
SLEEP_HEAVY;
// block until we have the mutex // block until we have the mutex
while (!*thread.has_mutex) while (!*thread.has_mutex)
SLEEP_LIGHT; ;
// enter the mutex // enter the mutex
std::cerr << "Hello from thread " << thread.id std::cout << "Hello from thread " << thread.id
<< "! (inside run loop, inside mutex)" << std::endl; << "! (inside run loop, inside mutex)" << std::endl;
SLEEP_LIGHT;
// increment the int (requires casting from void*)
*static_cast<int *>(thread.data) += 1; *static_cast<int *>(thread.data) += 1;
SLEEP_LIGHT;
// tell the thread manager we don't need the mutex // tell the thread manager we don't need the mutex
(*thread.wants_mutex) = false; (*thread.wants_mutex) = false;
SLEEP_HEAVY;
// mutex exit // mutex exit
} }
loops++; loops++;
if (max_loops < loops) { if (max_loops < loops)
SLEEP_HEAVY;
(*thread.is_finished) = true; (*thread.is_finished) = true;
}
} }
std::cerr << "Hello from thread " << thread.id << "! (done)" << std::endl; std::cout << "Hello from thread " << thread.id << "! (done)" << std::endl;
pthread_exit(nullptr); pthread_exit(nullptr);
} }
@ -151,54 +123,41 @@ void do_threading(struct thread_group threads) {
// spawn threads (none will enter the mutex yet) // spawn threads (none will enter the mutex yet)
for (size_t tid = 0; tid < threads.total_threads; ++tid) { for (size_t tid = 0; tid < threads.total_threads; ++tid) {
SLEEP_HEAVY;
pthread_create(&my_pthreads[tid], NULL, pthread_create(&my_pthreads[tid], NULL,
reinterpret_cast<PthreadFun>(thread_task_increment), reinterpret_cast<PthreadFun>(thread_task_increment),
&thread_data[tid]); &thread_data[tid]);
SLEEP_HEAVY;
} }
std::cerr << "Threads have been spawned." << std::endl; std::cout << "Threads have been spawned." << std::endl;
// loop until all threads are done // loop until all threads are done
for (size_t finished_threads = 0; finished_threads < threads.total_threads;) { 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 // TODO: make sure we cycle the mutex through threads round-robin style
// hand off the mutex to threads that want it // hand off the mutex to threads that want it
for (size_t tid_wants = 0; tid_wants < threads.total_threads; ++tid_wants) { for (size_t tid_wants = 0; tid_wants < threads.total_threads; ++tid_wants) {
SLEEP_LIGHT;
if (threads.wants_mutex[tid_wants]) { if (threads.wants_mutex[tid_wants]) {
// in case the mutex isn't used at all // in case the mutex isn't used at all
bool mutex_was_found = false; bool mutex_was_found = false;
SLEEP_LIGHT;
// find which thread has the mutex and hand it off if it's done // 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) { for (size_t tid_has = 0; tid_has < threads.total_threads; ++tid_has) {
if (threads.has_mutex[tid_has]) { if (threads.has_mutex[tid_has]) {
SLEEP_LIGHT;
// we found who has the mutex! // we found who has the mutex!
mutex_was_found = true; mutex_was_found = true;
SLEEP_LIGHT;
// is the thread still using the mutex? // is the thread still using the mutex?
if (!threads.wants_mutex[tid_has]) { if (!threads.wants_mutex[tid_has]) {
SLEEP_LIGHT;
// take the mutex from the thread that has it // take the mutex from the thread that has it
threads.has_mutex[tid_has] = false; threads.has_mutex[tid_has] = false;
SLEEP_LIGHT;
threads.can_ask_for_mutex[tid_has] = true; threads.can_ask_for_mutex[tid_has] = true;
SLEEP_HEAVY;
// give the mutex to the thread that wants it // give the mutex to the thread that wants it
threads.can_ask_for_mutex[tid_wants] = false; threads.can_ask_for_mutex[tid_wants] = false;
SLEEP_LIGHT;
threads.has_mutex[tid_wants] = true; threads.has_mutex[tid_wants] = true;
SLEEP_HEAVY;
} }
break; // no need to look at the rest if we found who has the mutex break; // no need to look at the rest if we found who has the mutex
@ -207,22 +166,16 @@ void do_threading(struct thread_group threads) {
// give the thread the mutex if it wasn't found to be in use // give the thread the mutex if it wasn't found to be in use
if (!mutex_was_found) { if (!mutex_was_found) {
SLEEP_LIGHT;
threads.has_mutex[tid_wants] = true; threads.has_mutex[tid_wants] = true;
SLEEP_LIGHT;
} }
SLEEP_LIGHT;
} }
} }
// find how many threads are done with the mutex // find how many threads are done with the mutex
finished_threads = 0; finished_threads = 0;
for (size_t tid = 0; tid < threads.total_threads; ++tid) { for (size_t tid = 0; tid < threads.total_threads; ++tid) {
if (threads.threads_finished[tid]) { if (threads.threads_finished[tid])
finished_threads += 1; finished_threads += 1;
SLEEP_LIGHT;
}
SLEEP_LIGHT;
} }
} }
@ -237,16 +190,10 @@ void do_threading(struct thread_group threads) {
int main(void) { int main(void) {
struct thread_group mythreads; struct thread_group mythreads;
// initialize random (for random sleep amounts)
srand(time(NULL));
// the count // the count
int count = 0; int count = 0;
// expected result std::cout << "Pre: " << DBG_PRINT(count) << std::endl;
const int count_expected = NUM_THREADS * (THREAD_LOOP_COUNT + 1) + count;
std::cout << "Expected: " << count_expected << std::endl;
mythreads.data = &count; mythreads.data = &count;
mythreads.total_threads = NUM_THREADS; mythreads.total_threads = NUM_THREADS;
@ -254,7 +201,5 @@ int main(void) {
do_threading(mythreads); do_threading(mythreads);
std::cout << "Got: " << count << std::endl; std::cout << "Post: " << DBG_PRINT(count) << std::endl;
return !(count_expected == count);
} }

View file

@ -2,7 +2,7 @@
# DO NOT RUN THIS, SOURCE IT FROM YOUR TERMINAL (idk why it needs this) # 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 timeout -s INT 0.8 ./mutex.out > /dev/null
# if grep -q 'Post: count: 99'; then # if grep -q 'Post: count: 99'; then
# : # good result # : # good result
@ -19,6 +19,9 @@ for ((i=0; i < 1000; ++i)); do
if ((i % 100 == 99)); then if ((i % 100 == 99)); then
echo -n . echo -n .
fi fi
if ((i % 1000 == 999)); then
echo " $((i + 1))"
fi
else else
echo -n "-ERROR on take ${i}: $e-" echo -n "-ERROR on take ${i}: $e-"
e= e=