Home > Mobile >  std::getline hangs even after stream is closed
std::getline hangs even after stream is closed

Time:01-21

I am setting up a (receiving) thread that uses std::getline on a fifo (named pipe). The peer may or may not be present (actually I do not get to the getline until I know there is a peer) and may or may not send data. Eventually, I may decide to shutdown the operation of the FIFO.

I need to be able to cleanup the receiving thread. But for this std::getline must return. I was hoping that doing a close on the std::ifstream would suffice. But it appears not to be the case.

I have this minimal case that hangs at the line rr.get()

  void test() {
    std::filesystem::path              path1("/tmp/f1");
    int                                retval = mkfifo(path1.string().c_str(), (unsigned int)0660);

    // Open TX side
    std::ofstream                      txStream;

    std::future<void>                  tt = std::async([&]() {
      txStream.open(path1, std::ios::binary | std::ios::out | std::ios::trunc);
      return;
    });

    // Open RX side
    std::ifstream                      rxStream;
    rxStream.open(path1, std::ios::binary | std::ios::in);

    // Setup getline
    std::future<void>                  rr = std::async([&]() {
      std::string                      line = "XX";
      std::getline(rxStream, line);
      std::cout << " Line is: " << line << std::endl;
    });

    std::this_thread::sleep_for(250ms);
    rxStream.close();

    if (tt.valid() == true) {
      tt.get();
      std::cout << "Done Tx" << std::endl;
    } else {
      std::cout << "Tx task not valid" << std::endl;
    }

    if (rr.valid() == true) {
      rr.get();
      std::cout << "Done Rx" << std::endl;
    } else {
      std::cout << "Rx task not valid" << std::endl;
    }
  }

Is there an alternative and reliable way to unblock std::getline?

CodePudding user response:

You have a data race in your program because

  1. C iostreams are not thread-safe.
    You can't close a stream while another thread is doing a read.

  2. POSIX file I/O is not thread-safe.
    You can't close a file descriptor while another thread is doing a read. Even if the Linux kernel is lenient about this, a blocking FIFO read can not be stopped from the RX side.

Rethink your design. FIFO's are thread-safe. Close the TX side of the FIFO, and the RX side will return EOF.

If you can't control the TX side at all, the best you can try is reading in non-blocking mode, this is outside the capabilities of C I/O streams.


Bonus notes:

  • you don't need to check tt.valid(), it is valid since you got it from std::async.
  • build with -fsanitize==thread to enable ThreadSanitizer. It'll tell you if you have a data race.
  • Related