0

I am trying to read the contents of the same file using 2 different threads in C++. Helgrind complains of a possible data race. Since reading the contents of the file does not change the state of the file, I do not understand why Helgrind is reporting a problem.

Below is Helgrind's message:

Possible data race during write of size 8 at 0x6647828 by thread #3
==10434== Locks held: none
==10434==    at 0x4C2C387: free (vg_replace_malloc.c:537)
==10434==    by 0x58E61B6: fclose@@GLIBC_2.2.5 (iofclose.c:84)
==10434==    by 0x50D454D: std::\__basic_file\<char\>::close() (basic_file.cc:277)
==10434==    by 0x510F6CD: std::basic_filebuf\<char, std::char_traits\<char\> \>::close() (fstream.tcc:166)
==10434==    by 0x510F9B4: \~basic_filebuf (fstream:220)
==10434==    by 0x510F9B4: std::basic_ifstream\<char, std::char_traits\<char\> \>::\~basic_ifstream() (fstream:499)
==10434==    by 0x401286: fn(void\*) (in a.out)
==10434==    by 0x4C30EEE: mythread_wrapper (hg_intercepts.c:387)
==10434==    by 0x4E43EA4: start_thread (pthread_create.c:307)
==10434==    by 0x597696C: clone (clone.S:111)
==10434==
==10434== This conflicts with a previous write of size 8 by thread #2
==10434== Locks held: none
==10434==    at 0x58F437F: \_IO_un_link (genops.c:81)
==10434==    by 0x58E61DC: fclose@@GLIBC_2.2.5 (iofclose.c:54)
==10434==    by 0x50D454D: std::\__basic_file\<char\>::close() (basic_file.cc:277)
==10434==    by 0x510F6CD: std::basic_filebuf\<char, std::char_traits\<char\> \>::close() (fstream.tcc:166)
==10434==    by 0x510F9B4: \~basic_filebuf (fstream:220)
==10434==    by 0x510F9B4: std::basic_ifstream\<char, std::char_traits\<char\> \>::\~basic_ifstream() (fstream:499)
==10434==    by 0x401286: fn(void\*) (in a.out)
==10434==    by 0x4C30EEE: mythread_wrapper (hg_intercepts.c:387)
==10434==    by 0x4E43EA4: start_thread (pthread_create.c:307)

Below is the code to reproduce the above issue:

#include <fstream>
#include <iostream>
#include <stdio.h>

using namespace std;

void* fn(void* parameter) {
        ifstream inputFile("sample", ios::in);
        while (!inputFile.eof()) {
                string line;
                getline(inputFile, line);
        }
        return NULL;
}

int main() {
        pthread_t t1, t2;
        pthread_create(&t1, NULL, fn, NULL);
        pthread_create(&t2, NULL, fn, NULL);
        pthread_join(t1, NULL);
        pthread_join(t2, NULL);
}

I tried to find out if reading a file changes the state of the file but I could not find any possible explanation. I am expecting that there is no data race when reading a file paralelly.

aga_stya
  • 11
  • 5
  • Suggest looking at https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons – 273K Jan 30 '23 at 17:05
  • 2
    Offtopic, but why are you still using pthreads. C++ has had native thread support since C++11 [std::async](https://en.cppreference.com/w/cpp/thread/async), [std::thread](https://en.cppreference.com/w/cpp/thread/thread). Use those toghether with std::mutex/std::soped_lock to avoid deadlocks due to missed mutex unlocks. Also useful to learn about lambdas and captures – Pepijn Kramer Jan 30 '23 at 17:13
  • @PepijnKramer I am working on a legacy code which does not support std::thread library yet. Using pthread_mutex_t also solves the above problem. I wanted to understand the reason why it is reporting the error. – aga_stya Jan 30 '23 at 17:15
  • Just an idea : do both instances of ifstream share the same underlying buffer ? (https://en.cppreference.com/w/cpp/io/basic_ifstream/rdbuf). If so that might be a reason. – Pepijn Kramer Jan 30 '23 at 17:18
  • At the risk of digressing from your original question : I've never used, pthread_mutex_t but it doesn't seem to be a RAII object (e.g. an object that will call unlock in its destructor). RAII hebavior ensures that whichever way you exit a scope the destructor gets called and unlock is called (also with errors/exceptions). – Pepijn Kramer Jan 30 '23 at 17:20
  • You are right. That is quite convenient. But the same could be achieved using pthread_mutex_lock and pthread_mutex_unlock. – aga_stya Jan 30 '23 at 17:22
  • Sure you can make a small wrapper class aound lock/unlock that does this :) – Pepijn Kramer Jan 30 '23 at 17:24
  • [related](https://stackoverflow.com/questions/20211935/is-ofstream-thread-safe) "Is ofstream thread safe?" Implementation dependent. – Déjà vu Jan 30 '23 at 17:53
  • I'm getting a different error from valgrind. What versions of gcc, valgrind and glibc are you using? – n. m. could be an AI Feb 04 '23 at 10:51
  • Valgrind: 3.16.1, gcc: 10.2.1, glibc: 2.17 – aga_stya Feb 06 '23 at 09:29

0 Answers0