Compare commits
5 commits
trunk
...
randomslee
Author | SHA1 | Date | |
---|---|---|---|
808c511a7b | |||
22a12e2995 | |||
160d22ded8 | |||
1043680b19 | |||
4a2c82247e |
1 changed files with 70 additions and 15 deletions
85
main.cpp
85
main.cpp
|
@ -3,17 +3,24 @@
|
|||
* @author Blake North <blake.north@digipen.edu>
|
||||
*/
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstring> // memset
|
||||
#include <iostream> // cout
|
||||
#include <cstddef> // size_t
|
||||
#include <cstring> // memset
|
||||
#include <ctime>
|
||||
#include <iostream> // cerr
|
||||
#include <vector> // vector
|
||||
|
||||
// used for threads
|
||||
#include <pthread.h> // pthread
|
||||
#include <unistd.h> // 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<int *>(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<PthreadFun>(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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue