Search code examples
cmultithreadingsynchronizationpthreadsposix

Race conditions problem, when synchronizing text files using threads


Well, I'm starting my studies on POSIX threads in C, and I'm trying to make each thread, read a line of files, up to and well, I managed to make it work using a synchronization through the condition variable, but when reading the first line of the file, two threads can read at the same time causing the race conditions problem, why does this happen? I tested it using valgrind's helgrind tool.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

/*

  $cat file.txt
  test1
  test2
  test3
  test4
  test5
  test6
*/

FILE *fp;

void *func(void) {

      char c;

      fp = fopen("file.txt", "r");
      if(!fp) {
          perror("DEBUG: ");
          exit(EXIT_FAILURE);
      }

      pthread_mutex_lock(&mutex);
      while((c = fgetc(fp)) != EOF) {
         printf("TH = %zu CHARACTER = %c\n", pthread_self(), c);
         if(c == '\n') {
              pthread_cond_wait(&cond, &mutex);
            } else {
              pthread_cond_signal(&cond);
          }
      }
      pthread_mutex_unlock(&mutex);

      pthread_exit(NULL);
 }

 int main(void) {

    pthread_t thread1, thread2;

    pthread_create(&thread1, NULL, (void *)func, NULL);
    sleep(1);
    pthread_create(&thread2, NULL, (void *)func, NULL);

    pthread_join(thread2, NULL);

    fclose(fp);

    return 0;
}  

Solution

  • Both threads are opening the file and assigning it to fp.

    After the first thread opens the file and reads the first entry, the next thread opens the file again and reads the first entry.

    Fix this by opening the file in the main function and passing the FILE * as a parameter to the thread function. This also fixes the thread function by giving it a signature that pthread_create expects, namely void *(*)(void *).

    void *func(void *param) {
    
          char c;
    
          FILE *fp = param;
          pthread_mutex_lock(&mutex);
          while((c = fgetc(fp)) != EOF) {
             printf("TH = %zu CHARACTER = %c\n", pthread_self(), c);
             if(c == '\n') {
                  pthread_cond_wait(&cond, &mutex);
                } else {
                  pthread_cond_signal(&cond);
              }
          }
          pthread_mutex_unlock(&mutex);
    
          pthread_exit(NULL);
     }
    
     int main(void) {
    
        pthread_t thread1, thread2;
    
          FILE *fp = fopen("file.txt", "r");
          if(!fp) {
              perror("DEBUG: ");
              exit(EXIT_FAILURE);
          }
    
        pthread_create(&thread1, NULL, func, fp);
        sleep(1);
        pthread_create(&thread2, NULL, func, fp);
    
        pthread_join(thread2, NULL);
    
        fclose(fp);
    
        return 0;
    }