I am working with a program to write the summation of 1-500 and 500-1000 using two separate threads. I need the output to be written in to a text file which is created by the program itself. When I run the program it creates the file according to the given name, but I am not getting the output as needed. It only writes one single line to the text file. That is the summation of 500-1000. But when I get the output using console it shows the answer as needed. How to overcome this problem. Thanks!
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <stdlib.h>
#define ARRAYSIZE 1000
#define THREADS 2
void *slave(void *myid);
/* shared data */
int data[ARRAYSIZE]; /* Array of numbers to sum */
int sum = 0;
pthread_mutex_t mutex;/* mutually exclusive lock variable */
int wsize; /* size of work for each thread */
int fd1;
int fd2;
FILE * fp;
char name[20];
/* end of shared data */
void *slave(void *myid)
{
int i,low,high,myresult=0;
low = (int) myid * wsize;
high = low + wsize;
for(i=low;i<high;i++)
myresult += data[i];
/*printf("I am thread:%d low=%d high=%d myresult=%d \n",
(int)myid, low,high,myresult);*/
pthread_mutex_lock(&mutex);
sum += myresult; /* add partial sum to local sum */
fp = fopen (name, "w+");
//printf("the sum from %d to %d is %d",low,i,myresult);
fprintf(fp,"the sum from %d to %d is %d\n",low,i,myresult);
printf("the sum from %d to %d is %d\n",low,i,myresult);
fclose(fp);
pthread_mutex_unlock(&mutex);
return;
}
main()
{
int i;
pthread_t tid[THREADS];
pthread_mutex_init(&mutex,NULL); /* initialize mutex */
wsize = ARRAYSIZE/THREADS; /* wsize must be an integer */
for (i=0;i<ARRAYSIZE;i++) /* initialize data[] */
data[i] = i+1;
printf("Enter file name : \n");
scanf("%s",name);
//printf("Name = %s",name);
fd1=creat(name,0666);
close(fd1);
for (i=0;i<THREADS;i++) /* create threads */
if (pthread_create(&tid[i],NULL,slave,(void *)i) != 0)
perror("Pthread_create fails");
for (i=0;i<THREADS;i++){ /* join threads */
if (pthread_join(tid[i],NULL) != 0){
perror("Pthread_join fails");
}
}
}
Its because you are opening the same file two times, one on each thread. They are overwriting each other's job.
To solve this you can:
Use the a+ mode on fopen() to append the new line to end of the existing file, or;
Open the file in main() and the threads will only fprintf() to it.
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);
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am trying to implement the first readers writers problem (reader's preference) in C. I am using mutex locks and unlocks to make sure that no writer can access the thread if a reader has a lock and any reader can access the thread if the first reader has a lock. Here is my code. I am unable to get my code till the end i.e., it is not reaching the thread join part. I guess I am getting a deadlock somewhere or maybe I am placing my mutex locks and unlocks in wrong place.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#define FALSE 0
#define TRUE 1
#define SLOWNESS 30000
#define INVALID_ACCNO -99999
#define SIZE 100
#define WRITE_ITR 100000
#define READ_ITR 100000
#define MAX_BALANCE 1000000
typedef struct {
int accno;
float balance;
} account;
// sleep function
void rest()
{
usleep(100);
}
//Global shared data structure
account account_list[SIZE]; /* this is the data structure that the readers and writers will be accessing concurrently.*/
pthread_mutex_t rw_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r_lock = PTHREAD_MUTEX_INITIALIZER;
/* Writer thread - will update the account_list data structure.
Takes as argument the seed for the srand() function.
*/
void * writer_thr(void * arg)
{
printf("Writer thread ID %ld\n", pthread_self());
srand(*((unsigned int *) arg)); /* set random number seed for this writer */
int i, j;
int r_idx;
unsigned char found; /* For every update_acc[j], set to TRUE if found in account_list, else set to FALSE */
account update_acc[WRITE_ITR];
/* first create a random data set of account updates */
for (i = 0; i < WRITE_ITR;i++)
{
r_idx = rand() % SIZE; /* a random number in the range [0, SIZE) */
update_acc[i].accno = account_list[r_idx].accno;
update_acc[i].balance = 1000.0 + (float) (rand() % MAX_BALANCE);
}//end for
/* open a writer thread log file */
char thr_fname[64];
snprintf(thr_fname, 64, "writer_%ld_thr.log", pthread_self());
FILE* fd = fopen(thr_fname, "w");
if (!fd)
{
fprintf(stderr,"Failed to open writer log file %s\n", thr_fname);
pthread_exit(&errno);
}//end if
/* The writer thread will now try to update the shared account_list data structure.
For each entry 'j' in the update_acc[] array, it will find the corresponding
account number in the account_list array and update the balance of that account
number with the value stored in update_acc[j].
*/
int temp_accno;
for (j = 0; j < WRITE_ITR;j++) {
found = FALSE;
for (i = 0; i < SIZE;i++) {
if (account_list[i].accno == update_acc[j].accno) {
found = 1;
temp_accno = account_list[i].accno;
pthread_mutex_lock(&rw_lock);
account_list[i].accno = INVALID_ACCNO;
account_list[i].balance = update_acc[j].balance;
account_list[i].accno = temp_accno;
rest(); /* makes the write long duration - SO AS TO INTRODUCE LATENCY IN WRITE before going for next 'j' */
pthread_mutex_unlock(&rw_lock);
fprintf(fd, "Account number = %d [%d]: old balance = %6.2f, new balance = %6.2f\n",
account_list[i].accno, update_acc[j].accno, account_list[i].balance, update_acc[j].balance);
}//end if
if (!found)
fprintf(fd, "Failed to find account number %d!\n", update_acc[j].accno);
} // end test-set for-loop
}
fclose(fd);
return NULL;
}
/* Reader thread - will read the account_list data structure.
Takes as argument the seed for the srand() function.
*/
void * reader_thr(void *arg)
{
printf("Reader thread ID %ld\n", pthread_self());
srand(*((unsigned int *) arg)); /* set random number seed for this reader */
int i, j;
int r_idx;
unsigned char found; /* For every read_acc[j], set to TRUE if found in account_list, else set to FALSE */
account read_acc[READ_ITR];
/* first create a random data set of account updates */
for (i = 0; i < READ_ITR;i++)
{
r_idx = rand() % SIZE; /* a random number in the range [0, SIZE) */
read_acc[i].accno = account_list[r_idx].accno;
read_acc[i].balance = 0.0; /* we are going to read in the value */
}//end for
/* open a reader thread log file */
char thr_fname[64];
snprintf(thr_fname, 64, "reader_%ld_thr.log", pthread_self());
FILE *fd = fopen(thr_fname, "w");
if (!fd)
{
fprintf(stderr,"Failed to reader log file %s\n", thr_fname);
pthread_exit(&errno);
}//end if
/* The reader thread will now try to read the shared account_list data structure.
For each entry 'j' in the read_acc[] array, the reader will fetch the
corresponding balance from the account_list[] array and store in
read_acc[j].balance. */
for (j = 0; j < READ_ITR;j++) {
/* Now read the shared data structure */
found = FALSE;
for (i = 0; i < SIZE;i++) {
rest();
if (account_list[i].accno == read_acc[j].accno) {
found = TRUE;
fprintf(fd, "Account number = %d [%d], balance read = %6.2f\n",
account_list[i].accno, read_acc[j].accno, read_acc[j].balance);
pthread_mutex_lock(&r_lock);
if(j == 1)
{
pthread_mutex_lock(&rw_lock);
}
pthread_mutex_unlock(&r_lock);
read_acc[j].balance = account_list[i].balance;
pthread_mutex_lock(&r_lock);
if(j == READ_ITR - 1)
{
pthread_mutex_unlock(&rw_lock);
}
pthread_mutex_unlock(&r_lock);
}
if (!found)
fprintf(fd, "Failed to find account number %d!\n", read_acc[j].accno);
} // end test-set for-loop
}
fclose(fd);
return NULL;
}
/* populate the shared account_list data structure */
void create_testset() {
time_t t;
srand(time(&t));
int i;
for (i = 0;i < SIZE;i++) {
account_list[i].accno = 1000 + rand() % RAND_MAX;
account_list[i].balance = 100 + rand() % MAX_BALANCE;
}
return;
}
void usage(char *str) {
printf("Usage: %s -r <NUM_READERS> -w <NUM_WRITERS>\n", str);
return;
}
int main(int argc, char *argv[])
{
time_t t;
unsigned int seed;
int i;
int READ_THREADS; /* number of readers to create */
int WRITE_THREADS; /* number of writers to create */
if(argc <= 3)
{
usage("./rw");
exit(EXIT_FAILURE);
}
int opt;
while((opt = getopt(argc, argv, "r:w:")) != -1)
{
switch(opt)
{
case 'r':
READ_THREADS = atoi(optarg);
break;
case 'w':
WRITE_THREADS = atoi(optarg);
break;
default:
usage("./rw");
exit(EXIT_FAILURE);
}
}
pthread_t* reader_idx = (pthread_t *) malloc(sizeof(pthread_t) * READ_THREADS); /* holds thread IDs of readers */
pthread_t* writer_idx = (pthread_t *) malloc(sizeof(pthread_t) * WRITE_THREADS); /* holds thread IDs of writers */
/* create readers */
for (i = 0;i < READ_THREADS;i++)
{
seed = (unsigned int) time(&t);
if((pthread_create(&reader_idx[i], NULL, reader_thr, &seed)) != 0)
{
perror("pthread reader create");
exit(-1);
}
}
printf("Done creating reader threads!\n");
/* create writers */
for (i = 0;i < WRITE_THREADS;i++)
{
seed = (unsigned int) time(&t);
/* YOUR CODE GOES HERE */
if((pthread_create(&writer_idx[i], NULL, writer_thr, &seed)) != 0)
{
perror("pthread writer create");
exit(-1);
}
}
printf("Done creating writer threads!\n");
/* Join all reader and writer threads.
*/
for(i = 0; i < READ_THREADS; i++)
{
pthread_join(reader_idx[i], NULL);
}
for(i = 0; i < WRITE_THREADS; i++)
{
pthread_join(writer_idx[i], NULL);
}
printf("Reader threads joined.\n");
printf("Writer threads joined.\n");
pthread_mutex_destroy(&r_lock);
pthread_mutex_destroy(&rw_lock);
return 0;
}
Your code is a mess. There are several things that are wrong with it and each one of them breaks the RW locking mechanism that you are trying to implement.
Both your reader threads and writer threads need to deal with reader exclusion and writer exclusion. Your current code completely ignores the reader exclusion in writer thread.
Your writer thread is reading from the shared structure (if (account_list[i].accno == update_acc[j].accno)) without excluding other writers.
I do not think this is implementable with just mutexes as you seem to be trying to do. E.g., last reader thread out of the critical section needs to be able to let waiting writers go. You probably need at least conditional variables or semaphores to do this.
My suggestion is to use the POSIX pthread_rwlock_init and friends instead.
If you insist on doing this yourself then please read at least this Concurrent Control with "Readers" and "Writers" paper for inspiration on how this can be implemented.
I'm creating simulation of a train station. Train information is read from a file. Each line of the file represents a train and each train gets its own thread. The main track of the train station can only hold 1 train at a time. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <readline/readline.h>
#include <unistd.h>
pthread_mutex_t main_track_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t main_track_condition_var = PTHREAD_COND_INITIALIZER;
int main_track_status = 0;
void *train_function(void *args) {
/* Parse train information */
int line_number = atoi(strtok(args, ":,"));
char *priority_direction = strtok(NULL,":,");
int loading_time = atoi(strtok(NULL, ":,"));
int crossing_time = atoi(strtok(NULL, ":,"));
/* Load train */
sleep(loading_time/10);
printf("Train %d is ready to go %s\n",line_number,priority_direction);
/* If the main track is currently in use, wait for it to become available */
while(main_track_status)
pthread_cond_wait(&main_track_condition_var, &main_track_mutex);
/* Use the main track */
pthread_mutex_lock(&main_track_mutex);
main_track_status = 1;
printf("Train %d is ON the main track going %s\n",line_number,priority_direction);
sleep(crossing_time/10);
main_track_status = 0;
/* Notify other trains main track is empty */
pthread_mutex_unlock(&main_track_mutex);
pthread_cond_signal(&main_track_condition_var);
printf("Train %d is OFF the main track after going %s\n",line_number,priority_direction);
pthread_exit(0);
}
int main() {
FILE *ptr_file;
char buff[10];
int train_count = 0;
char *train;
char line[15];
pthread_t trains[3];
ptr_file = fopen("./trains.txt", "r");
if (!ptr_file)
{
perror("fopen for trains.txt failed");
exit(EXIT_FAILURE);
}
/* Create train for each line of file */
while (fgets(buff,10, ptr_file)!=NULL) {
train = (char*)malloc(10 * sizeof(char));
/* Include line number from file in train information */
sprintf(line, "%d:", train_count);
strcat(line, buff);
strcpy(train, line);
if(pthread_create(&trains[train_count], NULL, &train_function, (void *) train))
{
perror("pthread create failed");
exit(EXIT_FAILURE);
}
train_count++;
}
fclose(ptr_file);
/* Wait for all trains to leave the station */
for (int x = 0; x < train_count; x++) {
pthread_join(trains[x], NULL);
}
free(train);
exit(EXIT_SUCCESS);
}
The trains input file:
e:10,6
W:5,7
E:3,10
The output of the program is:
Train 1 is ready to go W
Train 1 is ON the main track going W
Train 1 is OFF the main track after going W
Train 2 is ready to go E
Train 2 is ON the main track going E
Train 0 is ready to go e
Train 2 is OFF the main track after going E
I think my error lies in the train_function. As you can see, train 0 never gets access to the main track. I must be misunderstanding how threads are awoken with condition variables and am getting stuck in a deadlock. What am I missing?
Yes, you have a slight misunderstanding of pthread_cond_wait. The man page says:
The pthread_cond_timedwait() and pthread_cond_wait() functions shall
block on a condition variable. They shall be called with mutex locked
by the calling thread or undefined behavior results.
These functions atomically release mutex and cause the calling thread
to block on the condition variable cond;
So you need to lock before calling the pthread_cond_wait. That is, your code should be:
/* If the main track is currently in use, wait for it to become available */
pthread_mutex_lock(&main_track_mutex); /* SHOULD LOCK HERE */
while(main_track_status)
pthread_cond_wait(&main_track_condition_var, &main_track_mutex);
/* Use the main track */
//pthread_mutex_lock(&main_track_mutex); /* INCORRECT */
//<..SNIP..>
pthread_mutex_unlock(&main_track_mutex);
pthread_cond_signal(&main_track_condition_var);
I'm trying to alter a bit a code to use 'threads' instead of 'forks'. This is the code I have come up with, but there's an error message, and I'm unsure why.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>
#define N 2 /* define the total number of processes we want */
/* Set global variable */
float total=0;
/* compute function just does something. */
int compute()
{
int i;
float oldtotal=0, result=0;
/* for a large number of times just square root and square
the arbitrary number 1000 */
for(i=0;i<2000000000;i++)
{
result=sqrt(1000.0)*sqrt(1000.0);
}
/* Print the result should be no surprise */
printf("Result is %f\n",result);
/* We want to keep a running total in the global variable total */
oldtotal = total;
total = oldtotal + result;
/* Print running total so far. */
printf("Total is %f\n",total);
return(0);
}
void* thread_procedure(void* param)
{
int i = (int)param;
/* give a message about the proc ID */
printf("Process Id for process %d is %d\n",i,getpid());
/* call the function to do some computation. If we used sleep
The process would simply sleep. We do not want that */
compute();
return NULL;
}
int main()
{
int i, j;
pthread_t thread[N];
float result=0;
printf("\n"); /* bit of whitespace */
/* We want to loop to create the required number of processes
Note carefully how only the child process is left to run */
for(i=0;i<N;i++)
{
/* start new thread and catch it if it/one fails */
j = pthread_create(&thread[i], NULL, &thread_procedure, (void*)i);
if (j)
{
printf("Error");
exit(1);
}
}
/* joining with threads */
for(i=0;i<N;i++)
pthread_join(thread[i], NULL);
/* nothing else to do so end main function (and program) */
return 0;
}
This is the error that I receive, which I am not understanding
practical2b.c: In function ‘thread_procedure’:
practical2b.c:39:10: warning: cast from pointer to integer of different size
[-Wpointer-to-int-cast]
int i = (int)param;
^
practical2b.c: In function ‘main’:
practical2b.c:62:59: warning: cast to pointer from integer of different size
[-Wint-to-pointer-cast]
j = pthread_create(&thread[i], NULL, &thread_procedure, (void*)i);
^
/tmp/cc9tHCVO.o: In function `main':
practical2b.c:(.text+0x11c): undefined reference to `pthread_create'
practical2b.c:(.text+0x168): undefined reference to `pthread_join'
collect2: error: ld returned 1 exit status
After fixing all the errors/warnings raised by the compiler
and incorporating some of the earlier comments,
this is what the code would look like:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#define N 2 /* define the total number of processes we want */
/* Set global variable */
float total=0.0f;
/* compute function just does something. */
int compute()
{
int i;
float result=0.0f;
/* for a large number of times just square root and square
the arbitrary number 1000 */
for(i=0;i<2000000000;i++)
{
result=sqrt(1000.0)*sqrt(1000.0);
}
/* Print the result should be no surprise */
printf("Result is %f\n",result);
/* We want to keep a running total in the global variable total */
total += result;
/* Print running total so far. */
printf("Total is %f\n",total);
return(0);
} // end function: compute
void* thread_procedure(void* param)
{
int i = *(int*)param;
/* give a message about the proc ID */
printf("Process Id for process %d is %d\n",i,getpid());
/* call the function to do some computation. If we used sleep
The process would simply sleep. We do not want that */
compute();
return NULL;
}
int main()
{
int i, j;
pthread_t thread[N];
printf("\n"); /* bit of whitespace */
/* We want to loop to create the required number of processes
Note carefully how only the child process is left to run */
for(i=0;i<N;i++)
{
/* start new thread and catch it if it/one fails */
j = pthread_create(&thread[i], NULL, &thread_procedure, (void*)&i);
if (j)
{
printf("Error");
exit(1);
}
}
/* joining with threads */
for(i=0;i<N;i++)
pthread_join(thread[i], NULL);
/* nothing else to do so end main function (and program) */
return 0;
} // end function: main
I'm writing a program in c that basically copies files, but I'm getting this error: Segmentation fault (core dumped). From what I'm reading I think it's because I'm trying to access memory that hasn't been allocated yet. I'm a newbie when it comes to c and I suck at pointers, so I was wondering if you guys could tell me which pointer is causing this and how to fix it if possible. Btw, this program is supposed to be a daemon, but I haven't put anything inside the infinite while loop at the bottom.
Here is my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>
int main(int c, char *argv[]) {
char *source, *destination;
char *list1[30], *list2[30], *listDif[30];
unsigned char buffer[4096];
int i=0, x=0, sizeSource=0, sizeDest=0, sizeDif=0;
int outft, inft,fileread;
int sleeper;
struct dirent *ent, *ent1;
//Check number of arguments
if(c<3)
{
printf("Daemon wrongly called\n");
printf("How to use: <daemon name> <orginDirectory> <destinationDirectory> \n");
printf("or : <daemon name> <orginDirectory> <destinationDirectory> <sleeperTime(seconds)>");
return 0;
}
//Checks if sleeper time is given or will be the default 5minutes
/*if(c=4)
{
char *p;
errno = 0;
long conv = strtol(argv[3], &p, 10);
if(errno != 0 || *p != '\0')
{
printf("Number given for sleeper incorrect, it has to be an integer value.\n");
return(0);
} else
{
sleeper = conv;
}
} else
{
sleeper = 300;
}*/
//Get path of directories from arguments
source = argv[1];
destination = argv[2];
//Check if directories exist
DIR* dirSource = opendir(source);
if (!dirSource)
{
printf("Source directory incorrect\n");
return 0;
}
DIR* dirDest = opendir(destination);
if (!dirDest)
{
printf("Destination directory incorrect\n");
return 0;
}
/* save all the files and directories within directory */
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = strdup(ent->d_name);
sizeSource++;
if(sizeSource>=30){break;}
}
closedir(dirSource);
while((ent1 = readdir (dirDest)) != NULL) {
list2[sizeDest] = strdup(ent1->d_name);
sizeDest++;
if(sizeDest>=30){break;}
}
closedir(dirDest);
/* Verify the diferences between the directories and save them */
int z;
int dif = 0; //0 - False | 1 - True
printf("Diferenças:\n");
for(i=0;i<sizeSource;i++){
dif = 0;
for(z=0;z<sizeDest;z++){
if(strcmp(list1[i],list2[z])==0){ //If there is no match, it saves the name of the file to listDif[]
dif = 1;
break;
}
}
if(dif==0) {
printf("%s\n",list1[i]);
listDif[sizeDif] = list1[i];
sizeDif++;
}
}
/* This code will copy the files */
z=0;
while(z!=sizeDif){
// output file opened or created
char *pathSource, *pathDest;
strcpy(pathSource, source);
strcat(pathSource, "/");
strcat(pathSource, listDif[z]);
strcpy(pathDest, destination);
strcat(pathDest, "/");
strcat(pathDest, listDif[z]);
// output file opened or created
if((outft = open(pathDest, O_CREAT | O_APPEND | O_RDWR))==-1){
perror("open");
}
// lets open the input file
inft = open(pathSource, O_RDONLY);
if(inft >0){ // there are things to read from the input
fileread = read(inft, buffer, sizeof(buffer));
printf("%s\n", buffer);
write(outft, buffer, fileread);
close(inft);
}
close(outft);
}
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
//sleep(5); /* wait 5 seconds */
}
exit(EXIT_SUCCESS);
}
The result of ls is:
ubuntu#ubuntu:~/Desktop$ ls
Concatenar_Strings.c core D2 daemon.c examples.desktop
Concatenar_Strings.c~ D1 daemon daemon.c~ ubiquity.desktop
D1 and D2 are folders, and in D1 are three text documents that I want to copy into D2.
One other question, is this a delayed error or an immediate one? Because I doubt this message would appear on a code line that with two integers.
Thanks in advance guys.
This loop is wrong:
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = ent->d_name;
Probably, ent points to the same memory block every time, and the readdir function updates it. So when you save that pointer, you end up with your list containing invalid pointers (probably end up all pointing to the same string). Further, the string may be deallocated once you got to the end of the directory.
If you want to use the result of readdir after closing the directory or after calling readdir again you will need to take a copy of the data. In this case you can use strdup and it is usually good style to free the string at the end of the operation.
This may or may not have been the cause of your segfault. Another thing to check is that you should break out of your loops if sizeSource or sizeDest hits 30.
In the strcmp loop, you should really set dif = 0 at the start of the i loop, instead of in an else block.
Update: (more code shown by OP)
char *pathSource, *pathDest;
strcpy(pathSource, source);
You are copying to a wild pointer, which is a likely cause of segfaults. strcpy does not allocate any memory, it expects that you have already allocated enough.
One possible fix would be:
char pathSource[strlen(source) + 1 + strlen(listDif[z]) + 1];
sprintf(pathSource, "%s/%s", source, listDif[z]);
Alternatively (without using VLA):
char pathSource[MAX_PATH]; // where MAX_PATH is some large number
snprintf(pathSource, MAX_PATH, "%s/%s", source, listDif[z]);
Do the same thing for pathDest.
NB. Consider moving the closedir lines up to after the readdir loops; generally speaking you should open and close a resource as close as possible to the times you start and finish using them respectively; this makes your code easier to maintain.