Edited as per commenter's request.
This program creates two threads. Each thread reads from one of two specific input files, each of which contains either one letter or one '0' per line of code. The threads are supposed to read the letters into a global char array, which is then printed. The problem is that, upon reaching a '0,' the active thread must transfer control to the other thread, which should not have a '0' on that line. (We are sure that, if File 1 has a '0' on a line, then File 2, on the corresponding line, has a letter. Multiple zeros can follow one another, as can multiple letters.)
FILE ONE
h
0
h
0
h
0
h
0
h
0
FILE TWO
0
i
0
i
0
i
0
i
0
i
I am attempting to use pthread mutex lock/unlock as well as signal and wait to make this work. However, I keep reaching a state of deadlock.
There are two threads. Currently, they mirror each other meaning that they do the same things, just with different files and opposite conditions.
Thread Example:
char final[1001];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER;
int w = 1;
void *get()
{
//start reading
while (count < //number)
{
pthread_mutex_lock(&lock);
//read line
//if we've reached a zero
{
w = 2;
while(w == 2)
{
pthread_cond_wait(&condition1, &lock);
}
pthread_mutex_unlock(&lock);
}
else
{
if(w == 1)
{
if(strlen(placeHolderChars)>0)
{
placeHolderChars[1] = '\0';
}
//copy char to array
w= 2;
pthread_cond_signal(&condition2);
pthread_mutex_unlock(&lock);
}
}
if(feof(file))
{
fclose(file);
break;
}
count++;
}
return 0;
}
UPDATE: Signal-before-wait strategy did not really work when using a larger file. Still working on this!
This code seems to work. The primary significant change is to add pthread_cond_signal()
on the the other thread's condition before going into the while (who == N)
loops.
Other changes include basic debug printing to make it easier to see what's going on an which thread is doing what. Note that debugging messages end with newlines.
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern void *getMessage1(void *arg);
extern void *getMessage2(void *arg);
static char message[4096];
int main(void)
{
pthread_t id1;
pthread_t id2;
pthread_create((&id1), NULL, getMessage1, NULL);
pthread_create((&id2), NULL, getMessage2, NULL);
pthread_join(id1, NULL);
pthread_join(id2, NULL);
for (int j = 0; j < 1001 && message[j] != '\0'; j++)
printf("%c ", message[j]);
putchar('\n');
return 0;
}
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER;
static pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER;
static int who = 1;
void *getMessage1(void *arg)
{
assert(arg == NULL);
const char filename[] = "Student1";
FILE *studentOne = fopen(filename, "r");
if (studentOne == NULL)
{
fprintf(stderr, "Failed to open file %s for reading\n", filename);
exit(EXIT_FAILURE);
}
size_t howManyChars;
char *placeHolderChars;
int count = 1;
while (count < 501)
{
placeHolderChars = NULL;
if (getline(&placeHolderChars, &howManyChars, studentOne) == -1)
break;
printf("M1(%d): [%s]\n", count, placeHolderChars);
pthread_mutex_lock(&lock);
if (strcmp(placeHolderChars, "0\n") == 0)
{
printf("M1: Two's turn - 1\n");
pthread_cond_signal(&condition2);
who = 2;
while (who == 2)
{
pthread_cond_wait(&condition1, &lock);
}
free(placeHolderChars);
}
else
{
if (who == 1)
{
if (strlen(placeHolderChars) > 0)
{
placeHolderChars[1] = '\0';
}
strcat(message, placeHolderChars);
free(placeHolderChars);
who = 2;
pthread_cond_signal(&condition2);
}
else
printf("M1: Two's turn - 2\n");
}
pthread_mutex_unlock(&lock);
count++;
}
fclose(studentOne);
return 0;
}
void *getMessage2(void *arg)
{
assert(arg == NULL);
const char filename[] = "Student2";
FILE *studentTwo = fopen(filename, "r");
if (studentTwo == NULL)
{
fprintf(stderr, "Failed to open file %s for reading\n", filename);
exit(EXIT_FAILURE);
}
size_t howManyChars;
char *placeHolderChars;
int count = 0;
while (count < 501)
{
placeHolderChars = NULL;
if (getline(&placeHolderChars, &howManyChars, studentTwo) == -1)
break;
printf("M2(%d): [%s]\n", count, placeHolderChars);
pthread_mutex_lock(&lock);
if (strcmp(placeHolderChars, "0\n") == 0)
{
printf("M2: One's turn - 1\n");
pthread_cond_signal(&condition1);
who = 1;
while (who == 1)
{
pthread_cond_wait(&condition2, &lock);
}
free(placeHolderChars);
}
else
{
if (who == 2)
{
if (strlen(placeHolderChars) > 0)
{
placeHolderChars[1] = '\0';
}
strcat(message, placeHolderChars);
free(placeHolderChars);
who = 1;
pthread_cond_signal(&condition1);
}
else
printf("M2: One's turn - 2\n");
}
pthread_mutex_unlock(&lock);
count++;
}
fclose(studentTwo);
return 0;
}
You should be able to refine the code such that you pass a structure containing the relevant per-thread data (file name, current thread condition, other thread condition, maybe a 'thread ID') to a single function, so you have just getMessage()
.
Output:
M1(1): [h
]
M2(0): [0
]
M1(2): [0
]
M2: One's turn - 1
M1: Two's turn - 1
M2(1): [i
]
M2(2): [0
]
M2: One's turn - 1
M1(3): [h
]
M1(4): [0
]
M1: Two's turn - 1
M2(3): [i
]
M2(4): [0
]
M2: One's turn - 1
M1(5): [h
]
M1(6): [0
]
M1: Two's turn - 1
M2(5): [i
]
M2(6): [0
]
M2: One's turn - 1
M1(7): [h
]
M1(8): [0
]
M1: Two's turn - 1
M2(7): [i
]
M2(8): [0
]
M2: One's turn - 1
M1(9): [h
]
M1(10): [0
]
M1: Two's turn - 1
M2(9): [i
]
h i h i h i h i h i
I'm not completely happy with this code. I created a modified version with a single function used by both threads, as I hinted should be done, and modified the printing of the lines read to avoid printing the newlines (making the output more compact). Sometimes — not all the time — it would deadlock at the end. Two sample traces, one working, one deadlocking (program name pth47
):
$ pth47
M2(1): [0]
M2: 1's turn - 1
M1(1): [h]
M1(2): [0]
M1: 2's turn - 1
M2(2): [i]
M2(3): [0]
M2: 1's turn - 1
M1(3): [h]
M1(4): [0]
M1: 2's turn - 1
M2(4): [i]
M2(5): [0]
M2: 1's turn - 1
M1(5): [h]
M1(6): [0]
M1: 2's turn - 1
M2(6): [i]
M2(7): [0]
M2: 1's turn - 1
M1(7): [h]
M1(8): [0]
M1: 2's turn - 1
M2(8): [i]
M2(9): [0]
M2: 1's turn - 1
M1(9): [h]
M1(10): [0]
M1: 2's turn - 1
M2(10): [i]
h i h i h i h i h i
$ pth47
M1(1): [h]
M2(1): [0]
M1(2): [0]
M2: 1's turn - 1
M1: 2's turn - 1
M2(2): [i]
M2(3): [0]
M2: 1's turn - 1
M1(3): [h]
M1(4): [0]
M1: 2's turn - 1
M2(4): [i]
M2(5): [0]
M2: 1's turn - 1
M1(5): [h]
M1(6): [0]
M1: 2's turn - 1
M2(6): [i]
M1(7): [h]
M1(8): [0]
M2(7): [0]
M1: 2's turn - 1
M2: 1's turn - 1
M1(9): [h]
M1(10): [0]
M2(8): [i]
M1: 2's turn - 1
M2(9): [0]
M2: 1's turn - 1
^C
$
I've not tracked down the aberration. It isn't as simple as 'thread one went first'; there are examples where thread one went first and it completed fine.
I think the person who posed the question did it twice, a bit annoying. FWIW, here is my answer to the duplicate:
Pthread Synchronization: Back & Forth Reading of Two Text Files