Race Condition C thread - c

So two threads should call two runTimes functions and runTimes function should call increase_count and decrease_count. At the end the result should be 3. The problem is that the last line of code doesn't get executed when I run the program and I can't really identify what causes the race condition.
#define MAX_RESOURCES 5
int available_resources = MAX_RESOURCES;
int times = 100000;
pthread_mutex_t mutex;
sem_t semaphore;
/* decrease available_resources by count resources
* return 0 if sufficient resources available,
* otherwise return -1 */
int decrease_count(int count) {
if (available_resources < count) {
return -1;
} else {
available_resources -= count;
printf("Locked %i resources, now available: %i\n" , count , available_resources);
return 0;
}
}
/* increase available resources by count */
int increase_count(int count) {
if (count + available_resources > 5) {
return -1;
} else {
available_resources += count;
printf("Freed %i resources, now available: %i\n" , count , available_resources);
return 0;
}
}
void *runTimes(void *null) {
int i = 0 , result;
while (i < times) {
result = -1;
while (result < 0) {result = decrease_count(1);}
result = -1;
while (result < 0) {result = increase_count(1);}
i += 1;
printf("Count; %i\n",i );
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread1 , thread0;
pthread_t threads [2];
decrease_count(2);
pthread_create(&thread0, NULL, runTimes, NULL);
pthread_create(&thread1, NULL, runTimes, NULL);
int i = 0;
while( i < 2) {
pthread_join(threads[i], NULL);
i++;
}
pthread_exit(NULL);
printf("Currently available resources (should be 3): %i\n" , available_resources);
return 0;
}

the last line of code doesn't get executed
This is beacuse you call
pthread_exit(NULL);
before calling this
printf("Currently available resources (should be 3): %i\n" , available_resources);
(last) line.
pthread_exit() exits the current thread, that is the thread that calls function.
The race in the code you show is unrelated to this. It might occur because the code does not implement any protection against concurrently accessing the same variables.
Also you want to join the threads you created.
To do so change
pthread_create(&thread0, NULL, runTimes, NULL);
pthread_create(&thread1, NULL, runTimes, NULL);
to be
pthread_create(&threads[0], NULL, runTimes, NULL);
pthread_create(&threads[1], NULL, runTimes, NULL);

Related

Multithreaded Fibonacci Pair Program

I'm trying to write a program which creates two threads: a "front-end" and "back-end" thread. I want to create a "back-end" thread to iterate and compute pairs of terms from the fibonacci sequence and put them in an array, and a "front-end" thread that will print out the pairs of the array at each iteration.
"Front-End" Thread - For displaying result of "Back-End" thread operations in each iterations
"Back-End" Thread - For calculating and setting an array
ie. [5, 8], and after an iteration it will contain [13, 21]
I'm struggling to implement the Fibonacci sequence part in a thread and I've made the following progress:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
int fib;
void *front_end(void *ptr);
void *back_end(void *ptr);
int main() {
pthread_t thread1, thread2;
int arr[2] = {5,8};
const int *ptrtoarr;
ptrtoarr=arr;
int create1, create2;
int *s=(int *)(ptrtoarr);
printf("%d \n", *s);
ptrtoarr++;
s = (int *)(ptrtoarr);
printf("%d \n", *s);
ptrtoarr--;
create1 = pthread_create(&thread1, NULL, back_end, &arr);
if(create1) {
fprintf(stderr,"Error - pthread_create() return code: %d\n",create1);
exit(EXIT_FAILURE);
}
pthread_join(thread1, NULL);
//pthread_join(thread2, NULL);
}
// front-end thread to be callback for each back-end iteration
void *front_end(void *ptr) {
int *sum = ptr;
int i, upper = atoi(ptr);
if (upper > 0) {
for (i=0; i<upper; i++){
//Print the fib pairs
}
}
pthread_exit(0);
}
void *back_end(void *ptr) {
int i, upper = atoi(ptr);
fib=1;
if(upper > 0) {
int pre1 = 0;
int current;
//calc fib numbers.....
if(fib == 1){
printf("")
}
}
}
Can someone guide me through how I might approach this?
Your skeleton needs work.
Assuming the following:
unsigned n = ...; // How many to generate.
unsigned n_ready = 2; // How many are ready to print.
unsigned *fibs = malloc(sizeof(unsigned)*n);
fibs[0] = 0;
fibs[1] = 1;
At the core of your back end worker, you will have
for (unsigned i=2; i<n; ++i) {
fibs[i] = fibs[i-2] + fibs[i-1];
n_ready = i+1;
}
At the core of your frontend worker, you will have
for (unsigned i=0; i<n; ++i) {
while (i >= n_ready)
/* Nothing */;
printf("%u\n", fibs[i]);
}
Problem #1
You get into problems if a thread tries to read a variable when another is writing to it. Two or more threads reading the same variable at the same time is ok.
The variables used by both threads are n, the elements of fib[] and n_ready.
n:Not changed by either thread, so we don't need to control access to it.
fib[i] for i >= n_ready:Only accessed by the back end worker, so we don't need to control access to these.
fib[i] for i < n_ready:Only accessed by the frontend worker, so we don't need to control access to these.
n_ready:The back end worker could set n_ready at any time, and the frontend work could try to read n_ready at any time, so we do need to control access to n_ready.
Mutex are usually used to ensure that only one thread is accessing a resource (e.g. a variable, group of variables, file handle, etc) at a time.
Our back end worker becomes
for (unsigned i=2; i<n; ++i) {
// The mutex only protects n_ready
// --nothing else is going to touch fib[i-2] or fib[i-1] or fib[i]--
// so we don't need to obtain a lock yet.
fibs[i] = fibs[i-2] + fibs[i-1];
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = i+1;
pthread_mutex_unlock(&mutex);
}
Our frontend worker becomes
for (unsigned i=0; i<n; ++i) {
// We need to access n_ready.
pthread_mutex_lock(&mutex);
while (i >= n_ready) {
// Allow other thread to gain the lock.
pthread_mutex_unlock(&mutex);
// We need to access n_ready.
pthread_mutex_lock(&mutex);
}
// The mutex only protects n_ready
// --nothing is going to change fib[i]--
// so we can release it now rather than later.
pthread_mutex_unlock(&mutex);
printf("%u\n", fibs[i]);
}
Problem #2
You have a busy loop. In general, this is bad because it means your thread is using 100% doing nothing by waiting. (In this particular case, since i >= n_ready is probably already true, this would actually be a good strategy. But let's ignore that.) A thread can sleep until signaled by another thread using condition vars.
Our back end worker becomes
for (unsigned i=2; i<n; ++i) {
// The mutex only protects n_ready
// --nothing else is going to touch fib[i-2] or fib[i-1] or fib[i]--
// so we don't need to obtain a lock yet.
fibs[i] = fibs[i-2] + fibs[i-1];
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = i+1;
// Wake up the other thread if it's blocked.
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
Our frontend worker becomes
for (unsigned i=0; i<n; ++i) {
// We need to access n_ready.
pthread_mutex_lock(&mutex);
while (i >= n_ready)
pthread_cond_wait(&cond, &mutex);
// The mutex only protects n_ready
// --nothing is going to change fib[i]--
// so we can release it now rather than later.
pthread_mutex_unlock(&mutex);
printf("%u\n", fibs[i]);
}
Always call pthread_cond_wait on a locked mutex. It will unlock the mutex when it's called, and it will lock it before returning. This allows the other thread to obtain the mutex in order to change n_ready.
Complete code:
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define UNUSED(x) (void)(x)
// To control access to n_ready.
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static unsigned n_ready = 0; // How many are ready to print.
static unsigned n; // How many to generate.
static unsigned *fibs = NULL;
static void *back_worker(void *unused) {
UNUSED(unused);
fibs[0] = 0;
fibs[1] = 1;
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = 2;
// Wake up the other thread if it's blocked.
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
for (unsigned i=2; i<n; ++i) {
// The mutex only protects n_ready
// --nothing is going to touch fib[i]--
// so we don't need to obtain a lock yet.
fibs[i] = fibs[i-2] + fibs[i-1];
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = i+1;
// Wake up the other thread if it's blocked.
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
static void *front_worker(void *unused) {
UNUSED(unused);
for (unsigned i=0; i<n; ++i) {
// We need to access n_ready.
pthread_mutex_lock(&mutex);
while (i >= n_ready)
pthread_cond_wait(&cond, &mutex);
// The mutex only protects n_ready
// --nothing is going to change fib[i]--
// so we can release it now rather than later.
pthread_mutex_unlock(&mutex);
printf("%u\n", fibs[i]);
}
return NULL;
}
int main(void) {
n = 20; // How many to generate.
fibs = malloc(sizeof(unsigned) * n);
pthread_t back_thread;
if (errno = pthread_create(&back_thread, NULL, back_worker, NULL)) {
perror(NULL);
exit(1);
}
pthread_t front_thread;
if (errno = pthread_create(&front_thread, NULL, front_worker, NULL)) {
perror(NULL);
exit(1);
}
pthread_join(back_thread, NULL);
pthread_join(front_thread, NULL);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
free(fibs);
return 0;
}
Output:
$ gcc -Wall -Wextra -pedantic a.c -o a -lpthread && a
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
Suggestion for an exercise to apply the above
Create a pool of workers that print out the numbers placed into a queue. The output doesn't need to be in order.
The worker function is already written for you. You may not change the main or worker functions. I've even created the queue for you. You simply have to make it thread safe by modifying Queue_enqueue, Queue_dequeue and Queue_done functions. These are the only functions you may change.
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM_WORKERS 4
#define QUEUE_SIZE 10
#define NUM_ITEMS 40
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int done;
int empty;
int full;
size_t max;
size_t next_insert;
size_t next_read;
unsigned *buf;
} Queue;
static void Queue_init(Queue* q, size_t max) {
pthread_mutex_init(&(q->mutex), NULL);
pthread_cond_init(&(q->cond), NULL);
q->done = 0;
q->empty = 1;
q->full = 0;
q->max = max;
q->next_insert = 0;
q->next_read = 0;
q->buf = malloc(sizeof(unsigned)*max);
}
static void Queue_destroy(Queue *q) {
free(q->buf);
pthread_cond_destroy(&(q->cond));
pthread_mutex_destroy(&(q->mutex));
}
static void Queue_done(Queue *q) {
q->done = 1;
}
// Returns the oldest item from the queue (via a parameter) and returns 1.
// If the queue is empty and done, returns 0.
// If the queue is empty and not done, waits until that changes.
static int Queue_dequeue(Queue *q, unsigned *i) {
while (q->empty && !q->done) {
}
if (q->empty) {
// We are completely done.
return 0;
} else {
*i = q->buf[ q->next_read ];
q->next_read = ( q->next_read + 1 ) % q->max;
q->empty = q->next_read == q->next_insert;
q->full = 0;
return 1;
}
}
// Adds the argument to the queue.
// If the queue is full, waits until that changes.
static void Queue_enqueue(Queue *q, unsigned i) {
while (q->full && !q->done) {
}
if (q->done) {
fprintf(stderr, "Error: Attempted to add item to \"done\" queue.\n");
return;
}
q->buf[q->next_insert] = i;
q->next_insert = ( q->next_insert + 1 ) % q->max;
q->empty = 0;
q->full = q->next_insert == q->next_read;
}
static int msleep(long msec) {
struct timespec ts;
int res;
if (msec < 0) {
errno = EINVAL;
return -1;
}
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
do {
res = nanosleep(&ts, &ts);
} while (res && errno == EINTR);
return res;
}
// Protects access to stdout.
static pthread_mutex_t stdout_mutex;
static Queue q;
static void *worker(void *worker_id_) {
uintptr_t worker_id = (uintptr_t)worker_id_;
unsigned int seed = worker_id; // Whatever.
unsigned i;
while (Queue_dequeue(&q, &i)) {
pthread_mutex_lock(&stdout_mutex);
printf("[%" PRIuPTR "] Dequeued %u\n", worker_id, i);
pthread_mutex_unlock(&stdout_mutex);
// msleep( rand_r(&seed) % 1000 + 1000 ); // Simulate a 1 to 2s load.
pthread_mutex_lock(&stdout_mutex);
printf("[%" PRIuPTR "] Finished processing %u\n", worker_id, i);
pthread_mutex_unlock(&stdout_mutex);
}
return NULL;
}
int main(void) {
Queue_init(&q, QUEUE_SIZE);
pthread_t workers[NUM_WORKERS];
for (uintptr_t i=0; i<NUM_WORKERS; ++i) {
if (errno = pthread_create(&(workers[i]), NULL, worker, (void*)i)) {
perror(NULL);
exit(1);
}
}
for (unsigned i=0; i<NUM_ITEMS; ++i) {
pthread_mutex_lock(&stdout_mutex);
printf("[x] Enqueuing %u...\n", i);
pthread_mutex_unlock(&stdout_mutex);
Queue_enqueue(&q, i);
pthread_mutex_lock(&stdout_mutex);
printf("[x] Enqueued %u.\n", i);
pthread_mutex_unlock(&stdout_mutex);
}
Queue_done(&q);
pthread_mutex_lock(&stdout_mutex);
printf("[x] Called done.\n");
pthread_mutex_unlock(&stdout_mutex);
for (unsigned i=0; i<NUM_WORKERS; ++i)
pthread_join(workers[i], NULL);
Queue_destroy(&q);
pthread_mutex_destroy(&stdout_mutex);
return 0;
}
If you have questions about this, feel free to post a link to the question as a comment to this answer.
Solution to suggested excercise:
static void Queue_done(Queue *q) {
pthread_mutex_lock(&(q->mutex));
q->done = 1;
pthread_cond_signal(&(q->cond));
pthread_mutex_unlock(&(q->mutex));
}
// Returns the oldest item from the queue (via a parameter) and returns 1.
// If the queue is empty and done, returns 0.
// If the queue is empty and not done, waits until that changes.
static int Queue_dequeue(Queue *q, unsigned *i) {
pthread_mutex_lock(&(q->mutex));
while (q->empty && !q->done)
pthread_cond_wait(&(q->cond), &(q->mutex));
int dequeued;
if (q->empty) {
// We are completely done.
dequeued = 0;
} else {
*i = q->buf[ q->next_read ];
q->next_read = ( q->next_read + 1 ) % q->max;
q->empty = q->next_read == q->next_insert;
q->full = 0;
dequeued = 1;
}
pthread_cond_signal(&(q->cond));
pthread_mutex_unlock(&(q->mutex));
return dequeued;
}
// Adds the argument to the queue.
// If the queue is full, waits until that changes.
static void Queue_enqueue(Queue *q, unsigned i) {
pthread_mutex_lock(&(q->mutex));
while (q->full && !q->done)
pthread_cond_wait(&(q->cond), &(q->mutex));
if (q->done) {
fprintf(stderr, "Error: Attempted to add item to \"done\" queue.\n");
} else {
q->buf[q->next_insert] = i;
q->next_insert = ( q->next_insert + 1 ) % q->max;
q->empty = 0;
q->full = q->next_insert == q->next_read;
}
pthread_cond_signal(&(q->cond));
pthread_mutex_unlock(&(q->mutex));
}

Closing dynamically created threads

This is for an Operating Systems programming assignment. I'm attempting to read n number of files, use threads to search each file for a number of occurrences for a specific character.
./mycount j new.txt some.txt here.txt hello.txt
The output for my test code as is should be:
argumentCount: 6
threadCount: 4
pthread_create() for thread 0 returns: 0
Thread 1
pthread_create() for thread 1 returns: 0
Thread 2
pthread_create() for thread 2 returns: 0
Thread 3
pthread_create() for thread 3 returns: 0
Thread 4
However each execution of mycount is different, with the last thread usually not executing/printing. Either that or they'll print sporadically, in tandem, etc.
I'm attempting to utilize a mutex to ensure the integrity of my data but I'm not sure what's really happening behind the scenes.
How do I ensure that everything finishes the same way each time? The last thread always returns 0, but it sometimes won't execute the function I give it completely.
Code:
//GLOBALS
int occurrences = 0;
//PROTOTYPES
void *scanFile( void *filePtr );
//Initialize space for mutex.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//Receive arguments from .exe call
void main ( int argumentCount, char *argumentVariables[] )
{
//Exit if argumentCount is > 50.
if (argumentCount > 50)
{
perror("Too many arguments. Enter less than 50.\n");
exit(EXIT_FAILURE);
}
printf("argumentCount: %d \n", argumentCount);
//Instantiate variables.
//i - iterator
//*newCommand - Used to hold string value of first command entered.
//*newVector - Used to hold string of the rest of the commands. Is a vector.
int i;
char *searchCharacter;
char *newVector[argumentCount];
//Iterate through command line arguments and split them.
for (i = 0; i < argumentCount; i++)
{
searchCharacter = argumentVariables[1];
if (i < argumentCount - 1)
{
newVector[i] = argumentVariables[1 + i];
}
else
{
newVector[i] = NULL;
}
}
//Exit if newest command is NULL.
if (searchCharacter == NULL)
{
perror("No character entered!\n");
exit(EXIT_FAILURE);
}
int threads = argumentCount - 2;
printf("threadCount: %d \n", threads);
pthread_t * thread = malloc(sizeof(pthread_t)*threads);
for (int i = 0; i < threads; i++)
{
int ret;
char *message = "Thread";
ret = pthread_create(&thread[i], NULL, scanFile, (void*) message);
if (ret != 0)
{
printf("Error - pthread_create() return code: %d\n", ret);
exit(EXIT_FAILURE);
}
printf("pthread_create() for thread %d returns: %d\n", i, ret);
}
exit(EXIT_SUCCESS);
}
void *scanFile( void *filePtr )
{
pthread_mutex_lock( &mutex );
char *message;
message = (char *) filePtr;
occurrences += 1;
printf("%s %d\n", message, occurrences);
pthread_mutex_unlock( &mutex );
}
Found the solution thanks to user2864740 and Ken Thomases.
Added:
for (int j = 0; j < threads; j++)
{
//Join the threads so all the data is good to go.
pthread_join(thread[j], NULL);
}
Correction:
for (int i = 0; i < threads; i++)
{
request[i].file = argumentVariables[i + 2];
request[i].character = searchCharacter;
//Create the thread. Any output from the integer newThread is an error code.
newThread = pthread_create(&thread[i], NULL, *scanFile, &request[i]);
if (newThread != 0)
{
printf("Error - pthread_create() return code: %d\n", newThread);
exit(EXIT_FAILURE);
}
}
for (int j = 0; j < threads; j++)
{
//Join the threads so all the data is good to go.
pthread_join(thread[j], NULL);
}

WIN32 Thread Program Issue

This is my first time dealing with threads.
When I run the program without the GetCurrentThreadId() function it executes without any issue.
When I add that line of code it still executes but crashes once it reaches the end. Why is this?
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
static int tix[500];
static int done = 0;
HANDLE ghSemaphore;
DWORD WINAPI ThreadFunction();
int main(void)
{
DWORD threadID1, threadID2, threadID3, threadID4;
HANDLE hThread1, hThread2, hThread3, hThread4;
for (int i = 0; i < 500; i++) //initialize array
{
tix[i] = 0;
}
ghSemaphore = CreateSemaphore(NULL, 1, 10, NULL);
hThread1 = CreateThread(NULL, 0, ThreadFunction, NULL, 0, &threadID1);
hThread2 = CreateThread(NULL, 0, ThreadFunction, NULL, 0, &threadID2);
hThread3 = CreateThread(NULL, 0, ThreadFunction, NULL, 0, &threadID3);
hThread4 = CreateThread(NULL, 0, ThreadFunction, NULL, 0, &threadID4);
//printf("The thread ID: %d.\n", threadID1);
//printf("The thread ID: %d.\n", threadID2);
//printf("The thread ID: %d.\n", threadID3);
//printf("The thread ID: %d.\n", threadID4);
if (done = 1)
{
CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hThread3);
CloseHandle(hThread4);
}
for (int j = 0; j < 500; j++)
{
if (tix[j] = 0)
{
printf("not sold");
}
else if (tix[j] = 1)
{
printf("sold");
}
}
return 0;
}
DWORD WINAPI ThreadFunction()
{
WaitForSingleObject(ghSemaphore, 0);
printf("current thread running : %d\n", GetCurrentThreadId());
int i = 0;
if (done != 0) // if loop to test wether or not the array is full
{
while (tix[i] = 1) //traverse the array to find a open spot
{
i++;
}
tix[i] = 1;
}
if (i == 499) //if i is 499, set test variable to 1
{
done = 1;
return 0;
}
ReleaseSemaphore(ghSemaphore, 1, NULL);
}
Your thread function has incorrect signature. Thread takes one PVOID context argument:
DWORD WINAPI ThreadProc(
_In_ LPVOID lpParameter
);
Your threads could exit without releasing a semaphore. Also since you had initialized it with a value, that is greater than thread amount and never check WaitForSingleObject result, no synchronization is provided and multiple threads will modify a shared buffers in inconsistent manner. Even worse - nothing stops your program main thread exiting earlier, than ThreadFunction.
There is no return statement in the end of your thread function, so this is an undefined behavior. In fact it is a great wonder that your code even compiles. This entire approach to multithreading is incorrect and has to be remade from the scratch.

unable to reacquire mutex and pass values correctly between threads

I am trying to implement a code to practice synchronization, so might not be best design or approach but goal is as below
Main thread
Creates a payload of 100 integers and waits for any thread to be available
When it gets signal from a thread its available - it unlocks the payload for copying and proceeds to create another payload
Worker thread
on creation of it makes itself available for data processing and sends signal that its available
Tries to lock the data payload from main thread and copy it to local array
( observing bug here - not able to access data properly)
Turn off the sign of available
( unable to turn off available state to off)
Keep processing data through local copy
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#define WORKERS 2
#define ARRAY_ELEMENTS 100
#define MAX 1000
pthread_mutex_t mutex_bucket1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_signal = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_go = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_busy = PTHREAD_COND_INITIALIZER;
static int value = 0;
bool available = false;
void *worker_thread(void *pbucket)
{
sleep(5);
while(1)
{
unsigned int count = 0;
int local_array[ARRAY_ELEMENTS];
int *ptbucket = (int*)pbucket;
setbuf(stdout, NULL);
pthread_mutex_lock(&mutex_signal);
printf(" -------------- \n chainging state to available \n --------- ");
available = true;
printf(" -------------- \n from thread sending go signal \n --------- ");
pthread_cond_signal(&cond_go);
pthread_mutex_unlock(&mutex_signal);
pthread_mutex_lock(&mutex_bucket1);
printf(" -------------- \n data part locked in thread for copying \n --------- ");
while(count < ARRAY_ELEMENTS)
{
printf(" %d - \n", ptbucket[count]); /***incorrect values***/
local_array[count] = ptbucket[count];
count++;
}
pthread_mutex_unlock(&mutex_bucket1);
/*Never able to acquire mutex_signal and change state to not available*/ **BUG**
pthread_mutex_lock(&mutex_signal);
printf(" -------------- \n chainging state to not available \n --------- ");
available = false;
pthread_mutex_unlock(&mutex_signal);
count = 0;
while(count < ARRAY_ELEMENTS)
{
printf(" %d - \n", local_array[count]);
count++;
}
printf(" -------------- \n about to sleep for 5secs \n --------- ");
sleep(5);
}
}
int main(void)
{
pthread_t thread_id[WORKERS];
unsigned int* pbucket1 = (int*) malloc(sizeof(int) * ARRAY_ELEMENTS);
unsigned int* pbucket;
for(int i = 0; i < WORKERS - 1; i++)
{
pthread_create(&thread_id[i], NULL, worker_thread, (void *) pbucket);
}
for(int i = 0; i < MAX; i++)
{
unsigned int count = 0;
pbucket = pbucket1;
// Make the payload ready
pthread_mutex_lock(&mutex_bucket1);
printf(" -------------- creating data payload --------- \n");
while(count < ARRAY_ELEMENTS)
{
pbucket1[count] = i;
i++;
count++;
}
printf(" -------------- \n waiting for go signal \n --------- ");
while(!available)
{
pthread_cond_wait(&cond_go, &mutex_signal);
}
pthread_mutex_unlock(&mutex_bucket1);
/*I believe after we unlock variable "available" can be mutexed
again by other thread but seems thinking is flawed */
printf(" -------------- \n Main thread sleep for 3 seconds \n --------- ");
sleep(3);
}
for(int i = 0; i < WORKERS; i++)
{
pthread_join(thread_id[i], NULL);
}
return 0;
}
I think some of your idea is backwards; It shouldn't be the main context that is waiting, it should be the worker threads waiting for data ...
The job of the main thread should be to keep populating the payload and waking one thread at a time to process it.
So here's some scribbled code that is a little more sensible, I think:
/**
file: answer.c
compile: gcc -o answer answer.c -pthread
usage: answer [numThreads] [numElements]
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define STATE_WAIT 1
#define STATE_READY 2
void *routine(void*);
typedef struct _shared_t {
pthread_mutex_t m;
pthread_cond_t c;
unsigned char state;
int *payload;
size_t numElements;
pthread_t *threads;
size_t numThreads;
} shared_t;
static inline void shared_init(shared_t *shared, size_t numThreads, size_t numElements) {
memset(shared, 0, sizeof(shared_t));
pthread_mutex_init(&shared->m, NULL);
pthread_cond_init(&shared->c, NULL);
shared->state = STATE_WAIT;
shared->numThreads = numThreads;
shared->numElements = numElements;
{
int it = 0;
shared->threads = (pthread_t*) calloc(shared->numThreads, sizeof(pthread_t));
while (it < shared->numThreads) {
if (pthread_create(&shared->threads[it], NULL, routine, shared) != 0) {
break;
}
it++;
}
}
}
static inline void shared_populate(shared_t *shared) {
if (pthread_mutex_lock(&shared->m) != 0) {
return;
}
shared->payload = (int*) calloc(shared->numElements, sizeof(int));
{
int it = 0,
end = shared->numElements;
while (it < end) {
shared->payload[it] = rand();
it++;
}
}
shared->state = STATE_READY;
pthread_cond_signal(&shared->c);
pthread_mutex_unlock(&shared->m);
}
static inline void shared_cleanup(shared_t *shared) {
int it = 0,
end = shared->numThreads;
while (it < end) {
pthread_join(shared->threads[it], NULL);
}
pthread_mutex_destroy(&shared->m);
pthread_cond_destroy(&shared->c);
free(shared->threads);
}
void* routine(void *arg) {
shared_t *shared = (shared_t*) arg;
int *payload;
do {
if (pthread_mutex_lock(&shared->m) != 0) {
break;
}
while (shared->state == STATE_WAIT) {
pthread_cond_wait(&shared->c, &shared->m);
}
payload = shared->payload;
shared->state = STATE_WAIT;
pthread_mutex_unlock(&shared->m);
if (payload) {
int it = 0,
end = shared->numElements;
while (it < end) {
printf("Thread #%ld got payload %p(%d)=%d\n",
pthread_self(), payload, it, payload[it]);
it++;
}
free(payload);
}
} while(1);
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
shared_t shared;
int numThreads = argc > 1 ? atoi(argv[1]) : 1;
int numElements = argc > 2 ? atoi(argv[2]) : 100;
shared_init(&shared, numThreads, numElements);
do {
shared_populate(&shared);
} while (1);
shared_cleanup(&shared);
return 0;
}
Obviously, the code above is not very tolerant of errors, and is not easy to shutdown cleanly ... it's illustration only.
Let's first look at main so that we know what the flow of the main program is going to be:
int main(int argc, char *argv[]) {
shared_t shared;
int numThreads = argc > 1 ? atoi(argv[1]) : 1;
int numElements = argc > 2 ? atoi(argv[2]) : 100;
shared_init(&shared, numThreads, numElements);
do {
shared_populate(&shared);
} while (1);
shared_cleanup(&shared);
return 0;
}
It keeps a shared_t on the stack:
typedef struct _shared_t {
pthread_mutex_t m;
pthread_cond_t c;
unsigned char state;
int *payload;
size_t numElements;
pthread_t *threads;
size_t numThreads;
} shared_t;
Mostly self explanatory, mutex, condition and state are required for synchronization.
First of all the shared_t must be initialized with mutex, condition, state and threads using the provided options:
static inline void shared_init(shared_t *shared, size_t numThreads, size_t numElements) {
memset(shared, 0, sizeof(shared_t));
pthread_mutex_init(&shared->m, NULL);
pthread_cond_init(&shared->c, NULL);
shared->state = STATE_WAIT;
shared->numThreads = numThreads;
shared->numElements = numElements;
{
int it = 0;
shared->threads = (pthread_t*) calloc(shared->numThreads, sizeof(pthread_t));
while (it < shared->numThreads) {
if (pthread_create(&shared->threads[it], NULL, routine, shared) != 0) {
break;
}
it++;
}
}
}
When the worker threads are created by this routine, they are forced into a waiting state.
The first call to shared_populate in the loop awakens the first thread after setting the payload to some random numbers:
static inline void shared_populate(shared_t *shared) {
if (pthread_mutex_lock(&shared->m) != 0) {
return;
}
shared->payload = (int*) calloc(shared->numElements, sizeof(int));
{
int it = 0,
end = shared->numElements;
while (it < end) {
shared->payload[it] = rand();
it++;
}
}
shared->state = STATE_READY;
pthread_cond_signal(&shared->c);
pthread_mutex_unlock(&shared->m);
}
Note the use of pthread_cond_signal over pthread_cond_broadcast, because we only want to wake the first thread.
void* routine(void *arg) {
shared_t *shared = (shared_t*) arg;
int *payload;
do {
if (pthread_mutex_lock(&shared->m) != 0) {
break;
}
while (shared->state == STATE_WAIT) {
pthread_cond_wait(&shared->c, &shared->m);
}
payload = shared->payload;
shared->state = STATE_WAIT;
pthread_mutex_unlock(&shared->m);
if (payload) {
int it = 0,
end = shared->numElements;
while (it < end) {
printf("Thread #%ld got payload %p(%d)=%d\n",
pthread_self(), payload, it, payload[it]);
it++;
}
free(payload);
}
} while(1);
pthread_exit(NULL);
}
So we wake up in routine at the call to pthread_cond_wait, the state has changed, so we break out of the loop, we save the pointer to the payload, reset the state to WAIT, and release the mutex.
At this point main can repopulate the payload and awaken the next thread, meanwhile the current worker thread can process, and then free the payload.
Some advice:
Always use as few mutex and condition variables as possible (KISS)
Research the atomic nature of condition variables
Always follow the basic rules regarding acquisition and release of mutex and signaling of condition variables:
If you locked it, unlock it.
Only ever wait for something: predicated wait loops are absolutely required, all the time.
If you can't reproduce what I done, then take the code and try to expand upon it; The first thing you need to do is be able to shutdown the process gracefully (enter shared_cleanup), maybe you need a variable sized payload, or some other requirement not mentioned in the original question.
Note about printf ... appending to a stream is not guaranteed to be atomic, it so happens that most of the time on *nix it is ... since we are just doing show and tell, we don't need to care about that ... ordinarily, do not rely on atomicity for any stream operations ...

Multitasking how to make worker thread gain control after calling infinite loop function

assume creating 3 worker threads by pthread_create,
in these worker thread routine, each call a simple infinite loop function which do not have a return to do counting
how to make worker thread gain control after calling infinite loop function and save the context of infinite loop function for calling in worker thread again?
Let me rephrase to see if I understood the problem.
You have a master thread which spawns 3 worker threads which each do a long running (infinite) job.
At a certain point you want to interrupt processing, save the state of all threads to resume where they left off at a later time.
I think the best way of doing this is organize your threads work in transactionally bound chunks. When restarting, you check the last completed transaction, and go from there.
But since I suspect this to be a homework assignment in low level thread plumbing, may i suggest a shared boolean which is checked on every time you go through the loop to exit and store the state afterwards. Aternatively "kill" the thread and catch the exception and store the state. The last option is messy.
I think you should clarify your question.
If every worker thread calls an infinite loop then I suppose that your master thread would have to call pthread_cancel() on each of them. From what I gather this might require calls to other pthread_*() functions to set the "cancelability" of the target threads.
Of course this suggestion begs the question. The vastly preferable approach would be to prevent those infinite loops. Write your code so that it has exit conditions ... so that the work is bounded by some sort of input or has some sort of event handling.
want to do the effect of a threadpool, after calling infinite loop function, each worker thread can change other tasks(other infinite loop function) to run
for example 3 worker thread can run 4 tasks(infinite loop functions)
#ifndef JOB_CPP
#define JOB_CPP
#include "job.h"
#define NUM_OF_TASKS 4
#define NUM_OF_WORKERS 3
void (* job_queue[NUM_OF_TASKS])(void*);
void (* fp[NUM_OF_WORKERS])(void*); // original running job
int running_task[NUM_OF_WORKERS];
int idle[NUM_OF_TASKS];
int last_running_task[NUM_OF_WORKERS];
int no_of_tasks_running[NUM_OF_WORKERS];
my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
void func1(void *arg)
{
int count = 0;
int status;
while(true)
{
//if((count % 100) == 0)
//printf("func1 run %d\n", count);
count = count + 1;
//status = pthread_cond_signal(&data.cv);
}
}
void func2(void *arg)
{
int count = 0;
int status;
while(true)
{
//printf("func2 run %d\n", count);
count = count + 1;
//status = pthread_cond_signal(&data.cv);
}
}
void func3(void *arg)
{ int count = 0;
int status;
while(true)
{
//printf("func3 run %d\n", count);
count = count + 1;
//status = pthread_cond_signal(&data.cv);
}
}
void func4(void *arg)
{ int count = 0;
int status;
while(true)
{
//printf("func4 run %d\n", count);
count = count + 1;
//status = pthread_cond_signal(&data.done);
}
}
void jobinit()
{
for(int i=0; i<NUM_OF_TASKS; i++)
{
job_queue[i] = NULL;
idle[i] = 0;
}
for(int i=0; i<NUM_OF_WORKERS; i++)
{
fp[i] = NULL;
running_task[i] = 0;
last_running_task[i] = 0;
no_of_tasks_running[i] = 0;
}
jobadd(func1);
jobadd(func2);
jobadd(func3);
jobadd(func4);
jobrun();
}
void jobadd(void (*job)(void*))
{
for(int i=0; i<4; i++)
{
if(job_queue[i] == NULL)
{
job_queue[i] = job;
return;
}
}
}
void* workserver(void *arg);
void* workserver(void *arg)
{
int status, timedout;
struct timespec timeout;
status = pthread_mutex_lock(&data.mutex);
while(true)
{
timedout = 0;
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 2;
sleep(1);
//void (* clean)(void*);
status = pthread_cond_timedwait(&data.cv, &data.mutex, &timeout);
if(status == ETIMEDOUT){
printf("worker wait timed out %d\n", (int)arg);
timedout = 1;
}else if(status != 0){
printf("worker wait failed %d\n", (int)arg);
status = pthread_mutex_unlock(&data.mutex);
return NULL;
}
printf("workserver number: %d\n", (int)arg);
status = pthread_mutex_unlock(&data.mutex);
printf("function run %d\n", (int)arg);
(* job_queue[(int)arg])(NULL);
printf("cond wait start %d\n", (int)arg);
status = pthread_cond_wait(&data.done, &data.mutex);
printf("cond wait end\n");
status = pthread_mutex_lock(&data.mutex);
}
}
void jobrun()
{
for(int i=0; i<3; i++) {idle[i] = 0;}
pthread_t r1_threadid[3];
for(int i=0; i<3; i++)
{
pthread_create(&r1_threadid[i], NULL, workserver, (void*)i);
}
int status;
struct timespec timeout;
timeout.tv_sec = time (NULL) + 2;
timeout.tv_nsec = 0;
while(true)
{
status = pthread_mutex_lock(&data.mutex);
while(data.value == 0)
{
status = pthread_cond_timedwait(&data.cond, &data.mutex, &timeout);
}
if(data.value != 0)
{
//printf("condition was signaled\n");
data.value = 0;
}
status = pthread_mutex_unlock(&data.mutex);
if(status != 0)
printf("unlock mutex error");
}
}
#endif

Resources