Hello I am having trouble with strtol
(specifically, that implicit conversion loses integer precision)
as well as with sem_open incompatible pointer types passing sem_t
I have tried to use named semaphores instead of unnamed semaphores, use sem_open instead of sem_init, and use sem_close and sem_unlink instead of sem_destroy, but still getting errors.
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
// Global mutex and semaphores
pthread_mutex_t lock;
sem_t full;
sem_t empty;
// Global item to be incremented and enqueued
int global_value = 1;
// Queue
int* buffer;
int in_index;
int out_index;
// Producer and consumer arrays for comparing
int* producer_arr;
int* consumer_arr;
int p_idx = 0;
int c_idx = 0;
// Global args
int num_buffers;
int num_producers;
int num_consumers;
int items_produced;
int items_consumed;
int over_consume;
int over_consume_amount;
int p_time;
int c_time;
/*
* Function to remove item.
* Item removed is returned
*/
int dequeue_item() {
int item = buffer[out_index];
buffer[out_index] = 0;
out_index = (out_index + 1) % num_buffers;
return item;
}
/*
* Function to add item.
* Item added is returned.
* It is up to you to determine
* how to use the ruturn value.
* If you decide to not use it, then ignore
* the return value, do not change the
* return type to void.
*/
int enqueue_item(int item) {
buffer[in_index] = item;
in_index = (in_index + 1) % num_buffers;
return item;
}
void* producer(void* arg) {
int tid = *((int*) arg);
int item;
for (int i = 0; i < items_produced; i++) {
// Increment global item
item = global_value++;
// Lock semaphore and mutex
sem_wait(&empty);
pthread_mutex_lock(&lock);
// Produce item onto queue
enqueue_item(item);
producer_arr[p_idx++] = item;
printf( "%5d was produced by producer->\t%5d\n" , item, tid);
// Unlock mutex and semaphore
pthread_mutex_unlock(&lock);
sem_post(&full);
// Sleep
sleep(p_time);
}
pthread_exit(0);
}
void* consumer(void* arg) {
int tid = *((int*) arg);
int item;
if (!over_consume) {
// Either over or not required
for (int i = 0; i < items_consumed; i++) {
// Lock semaphore and mutex
sem_wait(&full);
pthread_mutex_lock(&lock);
// Consume item from queue
item = dequeue_item();
consumer_arr[c_idx++] = item;
printf( "%5d was consumed by consumer->\t%5d\n" , item, tid);
// Unlock mutex and semaphore
pthread_mutex_unlock(&lock);
sem_post(&empty);
// Sleep
sleep(c_time);
}
} else {
// Over consume if needed at the beginning of the program
over_consume = 0;
for (int i = 0; i < items_consumed + over_consume_amount; i++) {
// Lock semaphore and mutex
sem_wait(&full);
pthread_mutex_lock(&lock);
// Consume item from queue
item = dequeue_item();
consumer_arr[c_idx++] = item;
printf( "%5d was consumed by consumer->\t%5d\n" , item, tid);
// Unlock mutex and semaphore
pthread_mutex_unlock(&lock);
sem_post(&empty);
// Sleep
sleep(c_time);
}
}
pthread_exit(0);
}
/*
* $ ./pandc <N> <P> <C> <X> <Ptime> <Ctime>
* N = number of buffers to maintain
* P = number of producer threads
* C = number of consumer threads
* X = number of items each producer thread will produce
* Ptime = how long each producer thread will sleep after producing an item in seconds
* Ctime = how long each consumer thread will sleep after consuming an item in seconds
*/
int main(int argc, char** argv) {
// argv[0] is the program itself ("./pandc")
if (argc != 7) {
printf("Enter 6 arguments: \"$ ./pandc <N> <P> <C> <X> <Ptime> <Ctime>\"\n");
exit(EXIT_FAILURE);
}
// Print current time
time_t start_time = time(0);
printf( "Current time: %s\n" , ctime(&start_time));
// Read command-line args
num_buffers = strtol(argv[1], NULL, 10);
num_producers = strtol(argv[2], NULL, 10);
num_consumers = strtol(argv[3], NULL, 10);
items_produced = strtol(argv[4], NULL, 10);
items_consumed = (num_producers * items_produced) / num_consumers;
over_consume = (num_producers * items_produced) % num_consumers > 0 ? 1 : 0;
over_consume_amount = (num_producers * items_produced) - (num_consumers * items_consumed);
p_time = strtol(argv[5], NULL, 10);
c_time = strtol(argv[6], NULL, 10);
// Print producer-consumer problem information
printf("\t Number of Buffers : %5d\n", num_buffers);
printf("\t Number of Producers : %5d\n", num_producers);
printf("\t Number of Consumers : %5d\n", num_consumers);
printf("\tNumber of items Produced by each producer : %5d\n", items_produced);
printf("\tNumber of items Consumed by each consumer : %5d\n", items_consumed);
printf("\t Over consume on? : %5s\n", (over_consume) ? "yes" : "no");
printf("\t Over consume amount : %5d\n", over_consume_amount);
printf("\t Time each Producer Sleeps (seconds) : %5d\n", p_time);
printf("\t Time each Consumer Sleeps (seconds) : %5d\n", c_time);
printf("\n");
// Initialize mutex, semaphore, buffer, arrays
pthread_mutex_init(&lock, NULL); // mutex lock = 1;
sem_open( &full, 0, 0); // semaphore full = 0;
sem_open( &empty, 0, num_buffers); // semaphore empty = N;
buffer = malloc(sizeof(int*) * num_buffers); // buffer[N];
producer_arr = malloc(sizeof(int*) * num_producers * items_produced);
consumer_arr = malloc(sizeof(int*) * num_producers * items_produced);
// consumer_arr = malloc(sizeof(int*) * num_consumers * items_consumed);
in_index = 0;
out_index = 0;
// New threads
pthread_t producer_ids[num_producers];
pthread_t consumer_ids[num_consumers];
// Create producer and consumer threads
for (int i = 0; i < num_producers; i++) {
int id = i + 1;
pthread_create(&producer_ids[i], NULL, &producer, (void*) &id);
}
for (int i = 0; i < num_consumers; i++) {
int id = i + 1;
pthread_create(&consumer_ids[i], NULL, &consumer, (void*) &id);
}
// Join producer and consumer threads
for (int i = 0; i < num_producers; i++) {
pthread_join(producer_ids[i], NULL);
printf( "Producer thread joined:%5d\n" , i);
}
for (int i = 0; i < num_consumers; i++) {
pthread_join(consumer_ids[i], NULL);
printf( "Consumer thread joined:%5d\n" , i);
}
time_t end_time = time(0);
printf( "Current time: %s\n" , ctime(&end_time));
// Compare and match producer and consumer arrays
int match = 1; // Start out as true
fprintf(stderr, "Producer Array\t| Consumer Array\n");
for (int i = 0; i < num_producers * items_produced; i++) {
fprintf(stderr, "%d\t\t\t\t| %d\n", producer_arr[i], consumer_arr[i]);
if (producer_arr[i] != consumer_arr[i]) {
match = 0; // False when mismatch detected
}
}
fprintf(stderr, "\nConsume and Produce Arrays %s!\n", (match) ? "Match" : "DO NOT Match");
fprintf(stderr, "\nTotal Runtime: %d secs\n", (int) (end_time - start_time));
// Cleanup
pthread_mutex_destroy(&lock);
sem_unlink(&full);
sem_unlink(&empty);
free(buffer);
return 0;
}
Related
I have a producer consumer program that reads a file char by char and puts the content inside a buffer.
I need help with outputting the number of rounds the producer function made to deliver characters to the buffer. A round would mean one or more continuous writes to the buffer without being interrupted by wait (due to a full queue).
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
/*
This program provides a possible solution for producer-consumer problem using mutex and semaphore.
I have used 5 producers and 5 consumers to demonstrate the solution. You can always play with these values.
*/
#define MaxItems 5 // Maximum items a producer can produce or a consumer can consume
#define BufferSize 5 // Size of the buffer
sem_t empty;
sem_t full;
int in = 0;
int out = 0;
int buffer[BufferSize];
pthread_mutex_t mutex;
void *producer(void *pno)
{
int item;
for(int i = 0; i < MaxItems; i++) {
item = rand(); // Produce an random item
sem_wait(&empty);
pthread_mutex_lock(&mutex);
buffer[in] = item;
printf("Producer %d: Insert Item %d at %d\n", *((int *)pno),buffer[in],in);
in = (in+1)%BufferSize;
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
void *consumer(void *cno)
{
for(int i = 0; i < MaxItems; i++) {
sem_wait(&full);
pthread_mutex_lock(&mutex);
int item = buffer[out];
printf("Consumer %d: Remove Item %d from %d\n",*((int *)cno),item, out);
out = (out+1)%BufferSize;
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
}
int main()
{
pthread_t pro[5],con[5];
pthread_mutex_init(&mutex, NULL);
sem_init(&empty,0,BufferSize);
sem_init(&full,0,0);
FILE *fp = fopen("file.txt", "r");
if (fp != NULL) {
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
p1.BUFFER_SIZE = ftell(fp);
if (p1.BUFFER_SIZE == -1) { /* Error */ }
/* Allocate our buffer to that size. */
p1.item = malloc(sizeof(char) * (p1.BUFFER_SIZE + 1));
/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }
/* Read the entire file into memory. */
size_t newLen = fread(p1.item, sizeof(char), p1.BUFFER_SIZE, fp);
if ( ferror( fp ) != 0 ) {
fputs("Error reading file", stderr);
} else {
p1.item[newLen++] = '\0'; /* Just to be safe. */
}
}
int a[5] = {1,2,3,4,5}; //Just used for numbering the producer and consumer
for(int i = 0; i < 5; i++) {
pthread_create(&pro[i], NULL, (void *)producer, (void *)&a[i]);
}
for(int i = 0; i < 5; i++) {
pthread_create(&con[i], NULL, (void *)consumer, (void *)&a[i]);
}
for(int i = 0; i < 5; i++) {
pthread_join(pro[i], NULL);
}
for(int i = 0; i < 5; i++) {
pthread_join(con[i], NULL);
}
pthread_mutex_destroy(&mutex);
sem_destroy(&empty);
sem_destroy(&full);
return 0;
}
In general, there is no way to determine whether the time it takes for a call to sem_wait() to return is extended on account of the semaphore's value initially being zero. The sem_wait() function does not communicate that information.
One thing you could do, however, is to start out with a sem_trywait(), which will fail instead of blocking if it cannot immediately decrement the target semaphore. You could increment a counter in that case, and then proceed to perform a regular sem_wait(). Example:
int full_count = 0;
void *producer(void *pno) {
int my_num = *(int *)pno;
for(int i = 0; i < MaxItems; i++) {
int item = rand(); // Produce an random item
int result = sem_trywait(&empty);
if (result == -1) {
if (errno == EAGAIN) {
full_count += 1;
result = sem_wait(&empty);
// handle any error ...
} else {
// handle other error ...
}
}
pthread_mutex_lock(&mutex);
buffer[in] = item;
printf("Producer %d: Insert Item %d at %d\n", my_num, buffer[in], in);
in = (in + 1) % BufferSize;
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
Do note that there is no guarantee there that in the event that sem_wait() is called, it will actually block, because the semaphore can be incremented between the trywait and the wait. But it does tell you that if a wait had been performed instead of the trywait, then that wait would have blocked. In that case the producer is indeed delayed on account of a full buffer, even if it doesn't spend any of that delay blocked in sem_wait().
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <wait.h>
#include <pthread.h>
int item_to_produce, curr_buf_size;
int total_items, max_buf_size, num_workers, num_masters;
int consumed_items;
int *buffer;
pthread_mutex_t mutex;
pthread_cond_t has_data;
pthread_cond_t has_space;
void print_produced(int num, int master) {
printf("Produced %d by master %d\n", num, master);
}
void print_consumed(int num, int worker) {
printf("Consumed %d by worker %d\n", num, worker);
}
//consume items in buffer
void *consume_requests_loop(void *data)
{
int thread_id = *((int *)data);
while(1)
{
pthread_mutex_lock(&mutex); // mutex lock for consume
if(consumed_items == total_items) {
pthread_mutex_unlock(&mutex);
break;
}
while(curr_buf_size == 0) {
pthread_cond_wait(&has_data, &mutex);
}
print_consumed(buffer[(curr_buf_size--)-1], thread_id);
consumed_items++;
pthread_cond_signal(&has_space);
pthread_mutex_unlock(&mutex);
}
return 0;
}
//produce items and place in buffer
//modify code below to synchronize correctly
void *generate_requests_loop(void *data)
{
int thread_id = *((int *)data);
while(1) {
pthread_mutex_lock(&mutex); // mutex lock for consume
//all of items are produced
//master threads need to join
if(item_to_produce == total_items) {
pthread_mutex_unlock(&mutex);
break;
}
//there is no item to read
while (curr_buf_size == max_buf_size) {
pthread_cond_wait(&has_space, &mutex);
}
buffer[curr_buf_size++] = item_to_produce;
print_produced(item_to_produce, thread_id);
item_to_produce++;
pthread_cond_signal(&has_data);
pthread_mutex_unlock(&mutex); // mutex_produce unlock
}
return 0;
}
//write function to be run by worker threads
//ensure that the workers call the function print_consumed when they consume an item
int main(int argc, char *argv[])
{
int *master_thread_id; // array of master_thread_id
int *worker_thread_id; // array of worker_thread_id
pthread_t *master_thread; // array of master_thread
pthread_t *worker_thread; // array of worker_thread
item_to_produce = 0; // item will be produced by master_thread at next time
curr_buf_size = 0; // index of item will be saved in
consumed_items = 0;
int i;
if (argc < 5) {
printf("./master-worker #total_items #max_buf_size #num_workers #masters e.g. ./exe 10000 1000 4 3\n");
exit(1);
}
else {
num_masters = atoi(argv[4]);
num_workers = atoi(argv[3]);
total_items = atoi(argv[1]);
max_buf_size = atoi(argv[2]);
}
buffer = (int *)malloc (sizeof(int) * max_buf_size);
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&has_space, NULL);
pthread_cond_init(&has_data, NULL);
//create master producer threads
master_thread_id = (int *)malloc(sizeof(int) * num_masters);
master_thread = (pthread_t *)malloc(sizeof(pthread_t) * num_masters);
for (i = 0; i < num_masters; i++)
master_thread_id[i] = i;
for (i = 0; i < num_masters; i++)
pthread_create(&master_thread[i], NULL, generate_requests_loop, (void *)&master_thread_id[i]);
//create worker consumer threads
worker_thread_id = (int *)malloc(sizeof(int) * num_workers);
worker_thread = (pthread_t *)malloc(sizeof(pthread_t) * num_workers);
for (i = 0; i < num_workers; i++)
worker_thread_id[i] = i;
for (i = 0 ; i < num_workers; i++)
pthread_create(&worker_thread[i], NULL, consume_requests_loop, (void *)&worker_thread_id[i]);
//wait for all threads to complete
for (i = 0; i < num_masters; i++)
{
pthread_join(master_thread[i], NULL);
printf("master %d joined\n", i);
}
for (i = 0; i < num_workers; i++)
{
pthread_join(worker_thread[i], NULL);
printf("worker %d joined\n", i);
}
/*----Deallocating Buffers---------------------*/
free(buffer);
free(master_thread_id);
free(master_thread);
free(worker_thread_id);
free(worker_thread);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&has_data);
pthread_cond_destroy(&has_space);
return 0;
}
This code produces a number in the range of given numbers through the argument and consumes it.
But producer produces a number outside the range and doesn't join if it matches the condition. The consumer is too.
e.g when I give range of number like 0~39(total_item = 500), buff size 30(max_buf_size), num_workers 5, num_master 3, it doesn't produce and consume number only 0~39.
It produces and consumes numbers over 40.
In that way the thread is in a loop. To put the thread in sleep you can use, for example, the condition variables. (You can read this for more info https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html)
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));
}
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 ...
I have the bounded buffer, producer consumer problem to deal with and can only modify the prod and cons functions. This code runs without issues with only one consumer and producer threads. But with multiple of each it always gives me the same problem, sooner or later:
p5p1: p5p2a.c:207: bb_remove: Assertion `bbp->cnt > 0' failed.
What I don't get is how can this error occur when I check the bbp->cnt variable before calling the bbp_remove() function.
EDIT: The problem has been solved. I was not checking the variable within the lock, in either of the functions.
#include <sys/times.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#define DEBUG 0
#define BUF_SIZE 100000
#define ITER 10000000
#define PROD_THRD 3
#define CONS_THRD 3
#define USAGE_STRING "Usage: %s\n"
extern int errno;
/* This is the bounded buffer type */
typedef struct {
int cnt, in, out;
pthread_mutex_t lock; /* Mutex to avoid race conditions */
int buf[BUF_SIZE]; /* The data passed is the id of the
* producer */
} bbuf_t;
typedef struct {
bbuf_t *bbp;
int id;
} parg_t;
/*
* yield()
* Because there is no yield system call in Linux, what we
* do is to put the thread to sleep for 1 ms. Actually, it
* will sleep for at least 1/HZ, which is 10 ms in Linux/386.
*/
#define YIELD_s 0
#define YIELD_ns 1000000
void yield() {
struct timespec st = {YIELD_s, YIELD_ns};
if( (nanosleep(&st, NULL)) == -1 && (errno == EINVAL)) {
perror("nanosleep");
pthread_exit(NULL);
}
}
/* Initialize bounded buffer */
int bbuf_init(bbuf_t *bbp) {
if(bbp == NULL)
return 0;
bbp->in = 0;
bbp->out = 0;
bbp->cnt = 0;
pthread_mutex_init(&(bbp->lock), NULL); /* I do not understand, but the
* compiler complains when I use
* PTHREAD_MUTEX_INITIALIZER */
return 1;
}
/* Print the bounded buffer members that matter */
void print_bbuf(bbuf_t *bbp) {
printf("bbp->in = %d bbp->out = %d bbp_cnt = %d \n",
bbp->in, bbp->out, bbp->cnt);
}
/* To validate the value of the members in, out and cnt of bbuf_t */
int val(int n, int min, int max) {
return( (min <= n) && (n <= max));
}
/* Ensure that the values of the members in, out and cnt are consistent */
int consist(int cnt, int in, int out) {
return ( in == ((out + cnt) % BUF_SIZE) );
}
/* This is the code of the checker thread. It is used to ensure that
* the bounded buffer has not been corrupted.
* Every 100 ms it checks the values of: in, out and cnt.
* This thread exits either if it detects the buffer has been corrupted
* or if it does not detect any activity in 50 consecutive iterations,
* i.e. approximately 5s. */
/* These constants are used with nanosleep() and
* put a process to sleep for 100 ms */
#define SLEEP_s 0
#define SLEEP_ns 100000000
#define MAX_IDLE 50
void *check(void *arg) {
bbuf_t *bbp = arg;
int cnt[2], in[2], out[2]; /* values read */
int idle;
struct timespec st = {SLEEP_s, SLEEP_ns}; /* 100 ms */
while(1) {
pthread_mutex_lock( &(bbp->lock) );
in[1] = bbp->in;
out[1] = bbp->out;
cnt[1] = bbp->cnt;
pthread_mutex_unlock( &(bbp->lock) );
if(in[1] == in[0] && out[1] == out[0] && cnt[1] == cnt[0] ) {
idle++;
if( idle >= MAX_IDLE ) {
printf("Checking thread exiting:");
print_bbuf(bbp);
printf("\t no activity detected for some time.\n");
pthread_exit(NULL);
}
} else {
idle = 0;
}
if( !val(in[1], 0, BUF_SIZE - 1) ) {
printf("Invalid value in = %d \n", in[1]);
pthread_exit(NULL);
} else if ( !val(out[1], 0, BUF_SIZE - 1) ) {
printf("Invalid value out = %d \n", out[1]);
pthread_exit(NULL);
} else if ( !val(cnt[1], 0, BUF_SIZE) ) {
printf("Invalid value cnt = %d \n", cnt[1]);
pthread_exit(NULL);
} else if ( !consist(cnt[1], in[1], out[1]) ) {
printf("Inconsistent buffer: cnt = %d in = %d out = %d \n",
cnt[1], in[1], out[1]);
pthread_exit(NULL);
}
if( (nanosleep(&st, NULL) == -1) && (errno == EINVAL)) {
perror("nanosleep");
pthread_exit(NULL);
}
in[0] = in[1];
out[0] = out[1];
cnt[0] = cnt[1];
}
}
/* The producer threads may use this code to
* enter one item into the buffer */
void bb_enter(bbuf_t *bbp, int me) {
assert( bbp->cnt < BUF_SIZE);
(bbp->buf)[bbp->in] = me;
(bbp->in)++;
(bbp->in) %= BUF_SIZE;
(bbp->cnt)++;
//printf("%d\n",bbp->cnt);
}
/* This is the code for the producer threads.
*
* To avoid busy waiting (or at least too much busy waiting) the producers
* should yield, using the yield() defined above, if the buffer is
* full. In that case, they should print a debugging message as well.
*
* Each producer should produce ITER (10 M) items: an integer with
* the id it receives in prod()'s argument.
*/
void *prod(void *arg) {
parg_t *parg = (parg_t *)arg;
bbuf_t *bbp = parg->bbp;
int me = parg->id;
/* Add variables and code, if necessary */
printf("I am a producer and have started\n");
int gcnt = 0;
while( gcnt <= ITER ){
if(bbp->cnt < BUF_SIZE){
pthread_mutex_lock(&(bbp->lock));
bb_enter(bbp,me);
gcnt++;
pthread_mutex_unlock(&(bbp->lock));}
else if( bbp->cnt == (BUF_SIZE-1)) {printf("I shall produce yield()\n"); yield();}
}
printf("I am a producer and have ended\n");
return;
}
/* The consumer threads may use this function to
* remove an item */
int bb_remove(bbuf_t *bbp) {
int val;
assert(bbp->cnt > 0);
val = (bbp->buf)[bbp->out];
(bbp->out)++;
(bbp->out) %= BUF_SIZE;
(bbp->cnt)--;
return val;
}
/* This is the code for the consumer threads.
* To avoid busy waiting (or at least too much busy waiting) consumers
* should yield, using the yield() defined above, if the buffer is
* empty. In that case, they should print a debugging message as well.
*
* Each consumer should consume ITER (10 M) items, and keep track of the
* producers of the items it consumes: use the cnt[] below.
*/
void *cons(void *arg) {
bbuf_t *bbp = (bbuf_t *)arg;
int cnt[PROD_THRD];
/* Add variables and code, if necessary:
* do not forget to initialize cnt */
printf("I am a consumer and have started\n");
int i;
for(i = 0; i < PROD_THRD; i++){
cnt[i] = 0;}
int temp;
int gcnt = 0;
while( gcnt <= ITER ){
if(bbp->cnt > 0){
pthread_mutex_lock(&(bbp->lock));
temp = bb_remove(bbp);
gcnt++;
cnt[temp]++;
pthread_mutex_unlock(&(bbp->lock));}
else if( bbp->cnt == 0) {printf("I shall consume yield()\n"); yield();}
}
printf("I am a consumer and have ended\n");
return;
}
int main(int argc, char *argv[]) {
int i;
pthread_t *tid, ctid;
parg_t *parg;
bbuf_t *bbp;
/* This is to measure the time it takes to run the program */
struct tms t;
clock_t start, end;
long ticks = sysconf(_SC_CLK_TCK);
start = times(&t);
if( argc != 1 ) {
printf(USAGE_STRING, argv[0]);
exit(1);
}
/* Array for pthread_join() */
if((tid = (pthread_t *) malloc((PROD_THRD + CONS_THRD) * sizeof(pthread_t)))
== NULL ) {
printf("Out of memory.\n");
exit(2);
}
/* Allocate Bounded Buffer */
if((bbp = (bbuf_t *) malloc(sizeof(bbuf_t))) == NULL ) {
printf("Out of memory. \n");
exit(2);
}
/* Initialize Bounded Buffer */
if( bbuf_init(bbp) == 0 ) {
printf("Failed to initialize bounded buffer\n");
exit(3);
}
/* Arguments for producer threads */
if((parg = (parg_t *) malloc( PROD_THRD * sizeof (parg_t))) == NULL ) {
printf("Out of memory.\n");
exit(2);
}
/* Create checker thread */
if( pthread_create(&ctid, NULL, check, bbp) )
perror("pthread_create");
printf("Created checker thread %u\n", (unsigned)ctid);
/* Create consumer threads */
for( i = 0; i < CONS_THRD; i++ ) {
/* We pass the same data structure, the bounded buffer,
* to each consumer: they need to synchronize to access it */
if( pthread_create(tid+i, NULL, cons, bbp) )
perror("pthread_create");
printf("Created consumer thread %u\n", (unsigned)tid[i]);
}
/* Create producer threads */
for( i = 0; i < PROD_THRD; i++ ) {
/* Because we want each consumer to keep track of the
* producer of the items it consumes, we assign an
* id to each producer thread */
parg[i].bbp = bbp;
parg[i].id = i;
if( pthread_create(tid+(i+CONS_THRD), NULL, prod, parg+i) )
perror("pthread_create");
printf("Created producer thread %u (%d)\n", (unsigned)tid[i+CONS_THRD], i);
}
/* Join consumer and producer threads */
for( i = 0; i < CONS_THRD + PROD_THRD; i ++ ) {
if( pthread_join(tid[i], NULL) == 0 ) {
printf("Joined thread %u.\n", (unsigned)tid[i]);
} else {
printf("Failed to join thread %u\n", (unsigned)tid[i]);
}
}
/* Join checker thread */
if( pthread_join(ctid, NULL) == 0 ) {
printf("Joined checker thread %u.\n", (unsigned)ctid);
} else {
printf("Failed to join checker thread %u\n", (unsigned)ctid);
}
/* How long did it take to run this ? */
end = times(&t);
printf("Wall time %2.4f s \n", (float)(end - start) / ticks);
return 0;
}
You should enter the mutex lock before you check bbp->cnt. Since you are checking it before you enter the mutex, another thread can reduce the value before you acquire the mutex and try to reduce the value yourself.