I have this code that reads in two int numbers from a file. And stores it in a buffer[] to be taken in a second function to be used. I'm not sure if my stopping conditions in the first function are correct. It looks fine but when running the code it stops at the second function.
static int count = 0;
int buffer[5];
int requestNum = 1;
FILE* f;
int main(int argc, char** argv)
{
/*THREAD IDs*/
pthread_t liftR, lift1;
/*OPEN FILE*/
f = fopen("sim_input.txt", "r");
/*CREATE THREAD*/
pthread_create(&liftR, NULL, request, NULL);
pthread_create(&lift1, NULL, lift, NULL);
/*RUNS TILL THREADS FINISHED*/
pthread_join(liftR, NULL);
pthread_join(lift1, NULL);
/*CLEAN UP*/
fclose(f);
return 0;
}
void* request(void* data1)
{
int req1, req2, eof;
/*NOT EOF*/
while(eof != -1)
{
/*READ ONE REQUEST*/
eof = fscanf(f, "%d %d", &req1, &req2);
/*CHECK IF BUFFER FULLL*/
if(count < 5)
{
/*ADD REQUEST TO BUFFER*/
buffer[count] = req1;
buffer[count + 1] = req2;
count = count + 2;
printf("COUNT: %d\n", count);
/*PRINTING*/
printf("-------------------------------------------\n");
printf("From Buffer -> Item1: %d, Item2: %d\n", req1, req2);
printf("Request No: %d\n", requestNum);
printf("-------------------------------------------\n");
requestNum++;
}
}
return NULL;
}
void* lift(void* data2)
{
while(count > 0)
{
sleep(1);
printf("================\n");
printf("COUNT: %d\n", count);
printf("REMOVE ITEM FROM BUFFER - DO STUFF WITH IT\n");
printf("================\n");
count = count - 2;
}
return NULL;
}
OUTPUT:
Shows count 2, 4, 6. Only 3 request shown, In file it goes up to 10 request
There are multiple bugs in your program:
Reading uninitialized eof variable:
int int req1, req2, eof; // What is the value of eof? It could be -1 (or anything else).
/*NOT EOF*/
while(eof != -1)
You throw away the data you read:
if(count < 5)
{
/*ADD REQUEST TO BUFFER*/
}
If the first thread runs for a while before the second thread starts, then it will store the first two requests into the buffer, and throw away the rest.
To fix this, you need to wait for the second thread to drain the buffer when it is full.
You access count without any locking, which is a data race and undefined behavior. You must protect reading and writing shared (between threads) globals with a mutex.
Related
Essentially, my program creates 3 threads. The "server" and 2 "workers." The workers are meant to sum the 3 digit positive integers in a 1000 line file (500 numbers per thread). After each worker has summed its part, the server prints each workers total. The only problem is my semaphores are not seeming to work.
Here is a version of my program:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
// define semaphores
sem_t s1;
FILE *file;
int sum1 = 0, sum2 = 0, num1 = 0, num2 = 0;
// file name
char fileName[10] = "data1.dat";
// server routine
void* server_routine()
{
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 1\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 2\n\n");
// print the final results
printf("The sum of the first 500 numbers in the file is: %d\n", sum1);
printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2);
pthread_exit(NULL);
}
// thread 1 reoutine
void* t1_routine()
{
printf("Thread 1 recieved signal from server\n");
file = fopen(fileName, "r");
for(int i = 0; i < 500; i++)
{
fscanf(file, "%d", &num1);
sum1 += num1;
}
printf("sum in thread 1: %d\n", sum1);
printf("Thread 1 sends completion signal to server\n");
sem_post(&s1);
pthread_exit(NULL);
}
// thread 2 routine
void* t2_routine()
{
printf("Thread 2 recieved signal from server\n");
file = fopen(fileName, "r");
fseek(file, 500 * 5, SEEK_SET);
for(int i = 0; i < 500; i++)
{
fscanf(file, "%d", &num2);
sum2 += num2;
}
printf("sum in thread 2: %d\n", sum2);
printf("Thread 2 sends completion signal to server\n");
sem_post(&s1);
pthread_exit(NULL);
}
// main function
int main(int argc, char *argv[])
{
// define threads
pthread_t server, t1, t2;
// initialize the semaphore
sem_init(&s1, 0, 0);
if(pthread_create(&server, NULL, &server_routine, NULL) != 0)
{
return 1;
}
if(pthread_create(&t1, NULL, &t1_routine, NULL) != 0)
{
return 2;
}
if(pthread_create(&t2, NULL, &t2_routine, NULL) != 0)
{
return 3;
}
if(pthread_join(server, NULL) != 0)
{
return 4;
}
if(pthread_join(t1, NULL) != 0)
{
return 5;
}
if(pthread_join(t2, NULL) != 0)
{
return 6;
}
// destroy semaphores
sem_close(&s1);
// exit thread
pthread_exit(NULL);
// end
return 0;
}
I've tested with less threads more semaphores as well, with non luck. I've tried different initial semaphore values. The only time I can get the correct output is when I manually wait with sleep(5); but that defeats the purpose of this project.
A few issues ...
Each client thread does its own/private fopen but FILE *file; is global so they overwrite each others values.
We need to make this variable function scoped so each thread has its own private pointer.
There are no fclose calls.
pthread_exit should not be done by the main thread. It is only for threads created with pthread_create.
Otherwise ...
Whichever thread does the fopen last will set the final value.
So, there is a race condition and the effect is the same as if the main thread (prior to pthread_create calls) had done a single fopen, defeating the purpose of each thread doing its own fopen.
Worse yet, a given thread may do the first fopen, then start with fscanf and have its file value changed when the second thread replaces the file value, so weird stuff happens to each thread because they are doing fseek/fscanf on the same FILE * instance.
Having the aforementioned fclose calls would have made the issue more evident.
Here is the refactored code. It is annotated:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
// define semaphores
sem_t s1;
// NOTE/BUG: each thread opens a different stream, so this must be function
// scoped
#if 0
FILE *file;
#endif
int sum1 = 0,
sum2 = 0,
num1 = 0,
num2 = 0;
// file name
char fileName[10] = "data1.dat";
// server routine
void *
server_routine()
{
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 1\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 2\n\n");
// print the final results
printf("The sum of the first 500 numbers in the file is: %d\n", sum1);
printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2);
pthread_exit(NULL);
}
// thread 1 reoutine
void *
t1_routine()
{
// NOTE/FIX: this must be function scoped (i.e. private to this thread)
#if 1
FILE *file;
#endif
printf("Thread 1 recieved signal from server\n");
file = fopen(fileName, "r");
for (int i = 0; i < 500; i++) {
fscanf(file, "%d", &num1);
sum1 += num1;
}
printf("sum in thread 1: %d\n", sum1);
printf("Thread 1 sends completion signal to server\n");
sem_post(&s1);
#if 1
fclose(file);
#endif
pthread_exit(NULL);
}
// thread 2 routine
void *
t2_routine()
{
// NOTE/FIX: this must be function scoped (i.e. private to this thread)
#if 1
FILE *file;
#endif
printf("Thread 2 recieved signal from server\n");
file = fopen(fileName, "r");
fseek(file, 500 * 5, SEEK_SET);
for (int i = 0; i < 500; i++) {
fscanf(file, "%d", &num2);
sum2 += num2;
}
printf("sum in thread 2: %d\n", sum2);
printf("Thread 2 sends completion signal to server\n");
sem_post(&s1);
#if 1
fclose(file);
#endif
pthread_exit(NULL);
}
// main function
int
main(int argc, char *argv[])
{
// define threads
pthread_t server, t1, t2;
// initialize the semaphore
sem_init(&s1, 0, 0);
if (pthread_create(&server, NULL, &server_routine, NULL) != 0) {
return 1;
}
if (pthread_create(&t1, NULL, &t1_routine, NULL) != 0) {
return 2;
}
if (pthread_create(&t2, NULL, &t2_routine, NULL) != 0) {
return 3;
}
if (pthread_join(server, NULL) != 0) {
return 4;
}
if (pthread_join(t1, NULL) != 0) {
return 5;
}
if (pthread_join(t2, NULL) != 0) {
return 6;
}
// destroy semaphores
sem_close(&s1);
// exit thread
// NOTE/BUG: only a subthread should do this
#if 0
pthread_exit(NULL);
#endif
// end
return 0;
}
In the code above, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k
UPDATE:
Thank you for the response Craig. I have implemented your suggestions to my own code but nothing seemed to change. I then decided to copy paste your updated code into a c file to test it out and I got the same result. It is as follows (in a separate comment since the output is too long): –
Max
It's hard to compare results because we're using different datasets. I created a perl script to create some data.
Most important is that the sum reported by the given worker matches what the server sees for that worker task.
Then, if we know what each per thread section of the file should sum to, that is another matter.
The per line termination is critical (e.g.): CRLF vs LF (see below)
The actual order of worker sem_post and termination doesn't really matter. It can vary system to system or, even, invocation to invocation. What matters is that the server thread waits for N threads (i.e.) N sem_wait calls before printing any sums.
I've produced an updated version below.
Server does not "signal" a worker. The worker "signals" the server by doing sem_post and the server "receives" it by doing sem_wait
I've create a task/thread struct to hold the sums, thread IDs, etc.
I've generalized the code to allow N threads.
Added check of \n placement (i.e. line width). That is, under linux/POSIX a four digit number would be followed by LF (newline) and length would be 5. But, under windows, it would be CRLF (carriage return/newline) and length would be 6.
Added check of file size to ensure it is exactly the desired/expected length.
Some additional diagnostics.
Here is the updated code:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <sys/stat.h>
// number of bytes per line
// 5: 4 digits + LF
// 6: 4 digits + CRLF
#ifndef LINEWID
#define LINEWID (4 + 1)
#endif
// number of items / task
#ifndef COUNT
#define COUNT 500
#endif
// define semaphores
sem_t s1;
#if 0
int sum1 = 0,
sum2 = 0,
num1 = 0,
num2 = 0;
#endif
// file name
#if 0
char fileName[10] = "data1.dat";
#else
const char *fileName = "data1.dat";
#endif
// task control
typedef struct {
pthread_t tid; // thread ID
int tno; // thread index/offset
int sum; // sum
} tsk_t;
#define TSKMAX 50
int tskmax; // actual number of tasks
tsk_t tsklist[TSKMAX]; // list of tasks
// loop through all task blocks
#define TSKFORALL \
tsk_t *tsk = &tsklist[0]; tsk < &tsklist[tskmax]; ++tsk
// server routine
void *
server_routine(void *vp)
{
// NOTE/BUG: server does _not_ signal worker
#if 0
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
#endif
for (TSKFORALL) {
printf("Server waiting ...\n");
sem_wait(&s1);
printf("Server complete ...\n");
}
// print the final results
for (TSKFORALL)
printf("The sum of task %d is %d\n",tsk->tno,tsk->sum);
return (void *) 0;
}
// thread 1 reoutine
void *
worker_routine(void *vp)
{
FILE *file;
tsk_t *tsk = vp;
printf("Thread %d running ...\n",tsk->tno);
file = fopen(fileName, "r");
fseek(file,tsk->tno * COUNT * LINEWID,SEEK_SET);
tsk->sum = 0;
int num1;
int first = -1;
int last = -1;
for (int i = 0; i < COUNT; i++) {
if (fscanf(file, "%d", &num1) != 1) {
printf("Thread %d fscan error\n",tsk->tno);
break;
}
if (i == 0)
first = num1;
if (i == (COUNT - 1))
last = num1;
tsk->sum += num1;
}
printf("sum in thread %d: %d (first %d, last %d)\n",
tsk->tno, tsk->sum, first, last);
sem_post(&s1);
#if 1
fclose(file);
#endif
return (void *) 0;
}
// main function
int
main(int argc, char **argv)
{
--argc;
++argv;
setlinebuf(stdout);
setlinebuf(stderr);
if (argc != 1)
tskmax = 2;
else
tskmax = atoi(*argv);
if (tskmax > TSKMAX)
tskmax = TSKMAX;
// define threads
pthread_t server;
printf("main: %d tasks\n",tskmax);
printf("main: %d count\n",COUNT);
FILE *file = fopen(fileName,"r");
if (file == NULL) {
printf("main: fopen failure\n");
exit(96);
}
// check alignment
char chr;
fseek(file,LINEWID - 1,0);
fread(&chr,1,1,file);
if (chr != '\n') {
printf("main: newline mismatch -- chr=%2.2X\n",chr);
exit(97);
}
// get the file size
struct stat st;
if (fstat(fileno(file),&st) < 0) {
printf("main: fstat fault\n");
exit(97);
}
// ensure the file has the correct size
off_t size = tskmax * LINEWID * COUNT;
if (st.st_size != size)
printf("main: wrong file size -- st_size=%llu size=%llu\n",
(unsigned long long) st.st_size,
(unsigned long long) size);
fclose(file);
// initialize the semaphore
sem_init(&s1, 0, 0);
// set the offsets
int tno = 0;
for (TSKFORALL, ++tno)
tsk->tno = tno;
if (pthread_create(&server, NULL, &server_routine, NULL) != 0)
return 98;
for (TSKFORALL) {
if (pthread_create(&tsk->tid,NULL,worker_routine,tsk) != 0)
return 1 + tsk->tno;
}
if (pthread_join(server, NULL) != 0) {
return 99;
}
for (TSKFORALL) {
if (pthread_join(tsk->tid, NULL) != 0) {
return 5;
}
}
// destroy semaphores
sem_close(&s1);
// end
return 0;
}
Here is the perl script output that I used to generate the data:
number of tasks 2
element count per task 500
line separater 0A
section 0 sum 124750
section 1 sum 125250
Here is the program output:
main: 2 tasks
Server waiting ...
Thread 0 running ...
Thread 1 running ...
sum in thread 1: 125250 (first 1, last 500)
sum in thread 0: 124750 (first 0, last 499)
Server complete ...
Server waiting ...
Server complete ...
The sum of task 0 is 124750
The sum of task 1 is 125250
Here is the perl script:
#!/usr/bin/perl
# gendata -- generate data
#
# arguments:
# 1 - number of tasks (DEFAULT: 2)
# 2 - number of items / task (DEFAULT: 500)
# 3 - line separater (DEFAULT: \n)
master(#ARGV);
exit(0);
# master -- master control
sub master
{
my(#argv) = #_;
$tskmax = shift(#argv);
$tskmax //= 2;
printf(STDERR "number of tasks %d\n",$tskmax);
$count = shift(#argv);
$count //= 500;
printf(STDERR "element count per task %d\n",$count);
$sep = shift(#argv);
$sep //= "\n";
printf(STDERR "line separater");
foreach $chr (split(//,$sep)) {
$hex = ord($chr);
printf(STDERR " %2.2X",$hex);
}
printf(STDERR "\n");
for ($itsk = 0; $itsk < $tskmax; ++$itsk) {
$val = $itsk;
$sum = 0;
for ($lno = 1; $lno <= $count; ++$lno, ++$val) {
printf("%4d%s",$val,$sep);
$sum += $val;
}
printf(STDERR "section %d sum %d\n",$itsk,$sum);
}
}
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().
I'm trying to build a lift simulator in which a thread adds lift requests to the buffer and three threads representing lifts take the requests from the buffer and stimulate them simultaneously. I'm using pthread.h and semaphore.h to help me with the multithreaded nature of the program. As the requests need to be served in the order they are produced, I thought it would be best to use a queue as the buffer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <semaphore.h>
#define THREAD_NUM 4
sem_t semEmpty;
sem_t semFull;
pthread_mutex_t mutexBuffer;
pthread_cond_t cond;
pthread_cond_t condII;
// file pointer assignment statements.
FILE *input;
FILE *output;
int count = 0;
int totalRequests = 0;
int totalMovement = 0;
// A structure to store requests.
struct Request
{
int reqNo;
int from;
int to;
};
// A structure to store lift info.
struct Lift
{
int liftNo;
int c_pos;
int movement;
int tMovement;
int tRequests;
};
// Declaring and defining queue functions.
struct queue
{
int size;
int f;
int r;
struct Request *arr;
};
int isEmpty(struct queue *q)
{
if (q->r == q->f)
{
return 1;
}
return 0;
};
int isFull(struct queue *q)
{
if (q->r == q->size - 1)
{
return 1;
}
return 0;
};
void enqueue(struct queue *q, struct Request req)
{
if (isFull(q))
{
printf("This Queue is full\n");
}
else
{
q->r++;
q->arr[q->r] = req;
printf("Enqued element.\n");
}
};
struct Request dequeue(struct queue *q)
{
struct Request r;
if (isEmpty(q))
{
printf("This Queue is empty.\n");
}
else
{
q->f++;
r = q->arr[q->f];
printf("Dequed element.\n");
}
return r;
};
// Declaring Buffer.
struct queue Buffer;
void *request(void *args)
{
struct Request r1;
while (!feof(input))
{
fscanf(input, "%d %d %d", &r1.reqNo, &r1.from, &r1.to);
fprintf(output, "--------------------------------------------\n");
fprintf(output, "Request No: %d\n", r1.reqNo);
fprintf(output, "From Floor %d to Floor %d\n", r1.from, r1.to);
fprintf(output, "--------------------------------------------\n");
while (isFull(&Buffer) == 1)
{
pthread_cond_wait(&condII, NULL);
}
sem_wait(&semEmpty);
pthread_mutex_lock(&mutexBuffer);
enqueue(&Buffer, r1);
count++;
pthread_mutex_unlock(&mutexBuffer);
sem_post(&semFull);
}
}
void *lift(void *args)
{
pthread_mutex_lock(&mutexBuffer);
struct Lift currentLift = *(struct Lift *)args;
struct Request r1;
pthread_mutex_unlock(&mutexBuffer);
while (count > 0)
{
while(isEmpty(&Buffer) == 0){
sem_wait(&semFull);
pthread_mutex_lock(&mutexBuffer);
r1 = dequeue(&Buffer);
count--;
pthread_cond_broadcast(&condII);
currentLift.movement = 0; // Resetting movement.
currentLift.tRequests++; // Incrementing total requests for lift.
totalRequests++; // Incrementing total requests.
fprintf(output, "Lift %d Operations\nPrevious Position: %d Floor\nRequest: number %d from Floor %d to Floor %d\n", currentLift.liftNo, currentLift.c_pos, r1.reqNo, r1.from, r1.to);
fprintf(output, "Detail Operations:\n\tGo from Floor %d to Floor %d\n", currentLift.c_pos, r1.from);
// Stimulating lift movement.
sleep(1);
currentLift.movement += abs(currentLift.c_pos - r1.from);
currentLift.c_pos = r1.from;
fprintf(output, "\tGo from Floor %d to Floor %d\n", r1.from, r1.to);
sleep(1);
currentLift.movement += abs(r1.to - r1.from);
currentLift.c_pos = r1.to;
currentLift.tMovement += currentLift.movement; // Adding to total movement of lift.
fprintf(output, "\t#movement for this request: %d\n", currentLift.movement);
fprintf(output, "\t#Requests: %d\n", currentLift.tRequests);
fprintf(output, "\tTotal #movement: %d\n", currentLift.tMovement);
fprintf(output, "Current Position: %d Floor\n", r1.to);
pthread_mutex_unlock(&mutexBuffer);
sem_post(&semEmpty);
pthread_mutex_lock(&mutexBuffer);
totalMovement += currentLift.tMovement;
pthread_mutex_unlock(&mutexBuffer);
}
}
}
int main(int argc, char *argv[])
{
// Creating thread array, mutexes, semaphores, and lifts.
pthread_t threads[THREAD_NUM];
pthread_mutex_init(&mutexBuffer, NULL);
sem_init(&semEmpty, 0, 5);
sem_init(&semFull, 0, 0);
pthread_cond_init(&cond, NULL);
pthread_cond_init(&condII, NULL);
Buffer.size = 5;
Buffer.f = -1;
Buffer.r = -1;
Buffer.arr = malloc(sizeof(struct Request) * Buffer.size);
struct Lift l1, l2, l3;
// Setting values for lifts.
l1.liftNo = 1;
l2.liftNo = 2;
l3.liftNo = 3;
l1.c_pos = 1;
l2.c_pos = 1;
l3.c_pos = 1;
l1.tMovement = 0;
l2.tMovement = 0;
l3.tMovement = 0;
l1.tRequests = 0;
l2.tRequests = 0;
l3.tRequests = 0;
l1.movement = 0;
l2.movement = 0;
l3.movement = 0;
// Dynamically allocating lifts for use in threads.
struct Lift *lift1 = malloc(sizeof(struct Lift));
*lift1 = l1;
struct Lift *lift2 = malloc(sizeof(struct Lift));
*lift2 = l3;
struct Lift *lift3 = malloc(sizeof(struct Lift));
*lift3 = l2;
// assigning files to file pointers.
input = fopen("sim_input.txt", "r");
if (input == NULL)
{
printf("No file read.\n\n");
return -1;
}
else
{
fseek(input, 0, SEEK_SET);
printf("File read successfully.\n\n");
}
output = fopen("sim_out.txt", "w");
if (output == NULL)
{
printf("No file read.\n\n");
return -1;
}
else
{
printf("File read successfully.\n\n");
}
// Creating producer thread and joining.
if (pthread_create(&threads[0], NULL, &request, NULL) != 0)
{
perror("Failed to create thread");
}
if (pthread_join(threads[0], NULL) != 0)
{
perror("Failed to join thread");
}
// Creating threads and passing lift structures.
if (pthread_create(&threads[1], NULL, &lift, lift1) != 0)
{
perror("Failed to create thread");
}
if (pthread_create(&threads[2], NULL, &lift, lift2) != 0)
{
perror("Failed to create thread");
}
if (pthread_create(&threads[3], NULL, &lift, lift3) != 0)
{
perror("Failed to create thread");
}
// Joining all lifts using loop structure.
for (int i = 1; i < THREAD_NUM; i++)
{
if (pthread_join(threads[i], NULL) != 0)
{
perror("Failed to join thread");
}
}
// Printing total requests and total movements.
fprintf(output, "Total number of requests: %d\n", totalRequests);
fprintf(output, "Total number of movements: %d\n", totalMovement);
// Freeing memory.
if (count < 1)
{
sem_destroy(&semEmpty);
sem_destroy(&semFull);
pthread_cond_destroy(&cond);
pthread_cond_destroy(&condII);
pthread_mutex_destroy(&mutexBuffer);
fclose(input);
fclose(output);
free(lift1);
free(lift2);
free(lift3);
}
return 0;
}
Here's the complete code for the program. It requires sim_input.txt with the following contents:
1 1 5
2 7 2
3 3 8
4 4 11
5 12 15
6 2 9
7 11 7
8 8 15
9 12 19
10 20 7
I assume the program crashes at while(isEmpty(&Buffer) == 0) where I try to fetch the size of the Buffer(Queue) but there might be some other reason too, can you please help?
I assume the program crashes at
You shouldn't assume. Run your program under debugger and observe where it crashes.
Your question lacks MCVE, because your program requires input file sim_input.txt which you didn't provide.
There are obvious bugs though -- you are accessing Buffer from multiple threads, some but not all of these accesses are guarded by a mutex.
For a mutex to be effective, all accesses to variables shared between multiple threads must be guarded, or you will have a data race, which is undefined behavior.
Update:
The actual crash is happening here:
pthread_cond_wait(&condII, NULL);
This call makes absolutely no sense: you must provide a mutex to pthread_cond_wait, and it must be the same mutex as the one used in pthread_cond_signal or similar. Here it should be mutexBuffer, and the code should look something like:
pthread_mutex_lock(&mutexBuffer);
while (isFull(&Buffer)) {
pthread_cond_wait(&condII, &mutexBuffer);
}
// sem_wait() <<- this is bogus/unnecessary
enqueue(&Buffer, r1);
count++;
pthread_mutex_unlock(&mutexBuffer);
There are many other bugs as well, e.g. the code in lift() assumes that it will run after request has been enqueued, and will exit immediately if that hasn't happened.
In my runs, all 3 lift threads exit before a single request is processed.
I am learning how to use threads in C and have run into a problem when creating the threads. I am making a program that takes in 2 or more file names as command line arguments, counts the number of bytes in each file in their own thread, and then outputs the name of the largest file. When I use pthread_join() directly after creating a thread, the program runs as intended. However, I know this isn't how threads should be used because it defeats the purpose. When I use pthread_join() in a for loop after creating all the threads, then the program does not work correctly. Could anyone tell me what I am doing wrong? All help is appreciated. Here is my main function.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //mutex for changing max_bytes and max_name
int max_bytes = 0;
char max_name[100];
struct arg_struct{ //struct to hold args to pass the threads
int fd;
char name[100];
};
int main(int argc, char* argv[])
{
if(argc < 3){ //checks for correct number of arguments passed
perror("Wrong number of arguments");
return EXIT_FAILURE;
}
int arg_num = argc - 1; //holds number of arguments passed
pthread_t threadid[arg_num]; //array of thread IDs
struct arg_struct args;
for(int i = 0; i < arg_num; i++){
args.fd = open(argv[i+1], O_RDONLY);
memcpy(args.name, argv[i+1], sizeof(args.name)); //copies file name into arg_struct
int thread_err = pthread_create(&threadid[i], NULL, count_bytes, (void*)&args); //create thread by calling count_bytes and passing it a struct of args
//pthread_join(threadid[i], NULL);
if(thread_err != 0){
perror("pthread_create failed");
return EXIT_FAILURE;
}
}
for(int i = 0; i < arg_num; i++){
pthread_join(threadid[i], NULL);
}
printf("%s is the largest of the submitted files\n", max_name);
return 0;
}
This is the function that the threads are running.
void *count_bytes(void* arguments)
{
struct arg_struct *args = (struct arg_struct*)arguments; //casting arguments back to struct from void*
int fd = args -> fd;
char name[100];
memcpy(name, args -> name, sizeof(name)); //copies file name into name from args.name
int bytes = 0;
int size = 10;
char* buffer = (char*) malloc(size);
if(buffer == NULL){
perror("malloc failed");
exit(EXIT_FAILURE);
}
int buffer_count = 0;
for(int i = 0; i < size; i++){
buffer[i] = '\0'; //sets all elements to '\0' to determine end of file later
}
int read_return = read(fd, &buffer[buffer_count], 1);
if(read_return == -1){
perror("reading failed");
exit(EXIT_FAILURE);
}
while(buffer[buffer_count] != '\0'){
bytes++;
buffer_count++;
buffer[buffer_count] = '\0'; //sets previous element to '\0' to determine end of file later
if(buffer_count >= size){
buffer_count = 0; //buffer will hold up to 10 elements and then go back to the beginning
}
read_return = read(fd, &buffer[buffer_count], 1);
if(read_return == -1){
perror("reading failed");
exit(EXIT_FAILURE);
}
}
printf("%s has %d bytes\n", name, bytes);
pthread_mutex_lock(&mutex);
if(bytes > max_bytes){
max_bytes = bytes;
memcpy(max_name, name, sizeof(max_name));
}
//locks mutex to avoid race condition
//then sets bytes to max_bytes if it is later than max_bytes
//then locks mutex to allow another thread to have access
pthread_mutex_unlock(&mutex);
return NULL;
}
If it is of any use, these are the two outputs produced when it is running correctly
./a.out another buffered_readword.c
another has 8 bytes
buffered_readword.c has 3747 bytes
buffered_readword.c is the largest of the submitted files
And not correctly
./a.out another buffered_readword.c
buffered_readword.c has 1867 bytes
buffered_readword.c has 1881 bytes
buffered_readword.c is the largest of the submitted files
The problem is that there is only one args structure. After pthread_create is called the new thread may not run immediately. By the time the threads run it is likely that they will both see the same args values. Calling pthread_join inside the thread creation loop "fixes" that because it ensures each thread finishes before args is updated to the next value.
To fix properly pass a different args to each thread. Illustrative code to do that:
struct arg_struct args[arg_num];
for(int i = 0; i < arg_num; i++){
args[i].fd = open(argv[i+1], O_RDONLY);
memcpy(args[i].name, argv[i+1], sizeof(args[i].name));
int thread_err = pthread_create(&threadid[i], NULL, count_bytes, &args[i]);
....
I'm working on the producer and consumer problem. The producer is generating a random variable and placing it in the buffer. After this is done I want to print out the contents of the buffer. I also want to print the contents of the buffer after the consumer consumes a variable from the buffer. So just as an example,
Producer Thread 34567834 adds 43 to the buffer, and the current buffer contains 7, 29, 43
I am not sure of a way to print the contents of a buffer in one printf() statement. Thanks for any help.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
//Submit with screen shot of compiling and running code.
#define SIZE 20
#define NUMB_THREADS 10
#define PRODUCER_LOOPS 10
#define CONSUMER_LOOPS 2
#define TRUE 1
#define FALSE 0
typedef int buffer_t;
buffer_t buffer[SIZE];
int buffer_index;
pthread_mutex_t buffer_mutex;
/* initially buffer will be empty. full_sem
will be initialized to buffer SIZE, which means
SIZE number of producer threads can write to it.
And empty_sem will be initialized to 0, so no
consumer can read from buffer until a producer
thread posts to empty_sem */
sem_t full_sem; /* when 0, buffer is full */
sem_t empty_sem; /* when 0, buffer is empty. Kind of
like an index for the buffer */
/* sem_post algorithm:
mutex_lock sem_t->mutex
sem_t->value++
mutex_unlock sem_t->mutex
sem_wait algorithn:
mutex_lock sem_t->mutex
while (sem_t->value > 0) {
mutex_unlock sem_t->mutex
sleep... wake up
mutex_lock sem_t->mutex
}
sem_t->value--
mutex_unlock sem_t->mutex
*/
void insertbuffer(buffer_t value) {
if (buffer_index < SIZE) {
buffer[buffer_index++] = value;
} else {
printf("Buffer overflow\n");
}
}
buffer_t dequeuebuffer() {
if (buffer_index > 0) {
return buffer[--buffer_index]; // buffer_index-- would be error!
} else {
printf("Buffer underflow\n");
}
return 0;
}
int isempty() {
if (buffer_index == 0)
return TRUE;
return FALSE;
}
int isfull() {
if (buffer_index == SIZE)
return TRUE;
return FALSE;
}
void *producer2(void *thread_n) {
int thread_numb = *(int *)thread_n;
buffer_t value;
int i=0;
while (i++ < PRODUCER_LOOPS) {
sleep(rand() % 10);
value = rand() % 100;
pthread_mutex_lock(&buffer_mutex);
do {
// cond variables do the unlock/wait and wakeup/lock atomically,
// which avoids possible race conditions
pthread_mutex_unlock(&buffer_mutex);
// cannot go to slepp holding lock
sem_wait(&full_sem); // sem=0: wait. sem>0: go and decrement it
// there could still be race condition here. another
// thread could wake up and aqcuire lock and fill up
// buffer. that's why we need to check for spurious wakeups
pthread_mutex_lock(&buffer_mutex);
} while (isfull()); // check for spurios wake-ups
insertbuffer(value);
pthread_mutex_unlock(&buffer_mutex);
sem_post(&empty_sem); // post (increment) emptybuffer semaphore
//printf("Producer Thread %d adds %d added %d to buffer\n", pthread_self(), thread_numb, value);
printf("Producer Thread %d adds %d to the buffer, and the current buffer contains %d \n", pthread_self(), value, *buffer);
}
pthread_exit(0);
}
void *consumer2(void *thread_n) {
int thread_numb = *(int *)thread_n;
buffer_t value;
int i=0;
while (i++ < CONSUMER_LOOPS) {
pthread_mutex_lock(&buffer_mutex);
do {
pthread_mutex_unlock(&buffer_mutex);
sem_wait(&empty_sem);
pthread_mutex_lock(&buffer_mutex);
} while (isempty()); //check for spurios wakeups
value = dequeuebuffer(value);
pthread_mutex_unlock(&buffer_mutex);
sem_post(&full_sem); // post (increment) fullbuffer semaphore
printf("Consumer Thread %d dequeue %d from buffer, and the current buffer contains %d \n", pthread_self(), value, *buffer);
}
pthread_exit(0);
}
int main(int argc, int **argv) {
buffer_index = 0;
pthread_mutex_init(&buffer_mutex, NULL);
sem_init(&full_sem, // sem_t *sem
0, // int pshared. 0 = shared between threads of process, 1 = shared between processes
SIZE); // unsigned int value. Initial value
sem_init(&empty_sem,
0,
0);
/* full_sem is initialized to buffer size because SIZE number of
producers can add one element to buffer each. They will wait
semaphore each time, which will decrement semaphore value.
empty_sem is initialized to 0, because buffer starts empty and
consumer cannot take any element from it. They will have to wait
until producer posts to that semaphore (increments semaphore
value) */
pthread_t thread[NUMB_THREADS];
int thread_numb[NUMB_THREADS];
int i;
for (i = 0; i < NUMB_THREADS; ) {
thread_numb[i] = i;
if(i <= 2)
{
pthread_create(thread + i, // pthread_t *t
NULL, // const pthread_attr_t *attr
producer2, // void *(*start_routine) (void *)
thread_numb + i); // void *arg
}
thread_numb[i] = i;
// playing a bit with thread and thread_numb pointers...
pthread_create(&thread[i], // pthread_t *t
NULL, // const pthread_attr_t *attr
consumer2, // void *(*start_routine) (void *)
&thread_numb[i]); // void *arg
i++;
}
for (i = 0; i < NUMB_THREADS; i++)
pthread_join(thread[i], NULL);
pthread_mutex_destroy(&buffer_mutex);
sem_destroy(&full_sem);
sem_destroy(&empty_sem);
return 0;
}
You can't print an array of unknown length in one line, but you can modify your insertbuffer to show the details every time you insert a value. Or, implement the printing as a separate function. Then calling it is a one-liner, obviously.
void insertbuffer(int threadnum, buffer_t value) {
int i;
if (buffer_index < SIZE) {
buffer[buffer_index++] = value;
printf("Producer Thread %d adds %d to the buffer, and the current buffer contains",
threadnum, (int)value);
for(i=0; i<buffer_index; i++) {
if(i)
printf(",");
printf(" %d", (int)buffer[i]);
}
printf("\n");
} else {
printf("Buffer overflow\n");
}
}