Question

If I have a pipe to run some command, the piped command needs to do some cleanup, however, if the processes that started the pipe has an error, the piped command is not cleaning up. Is the piped command getting SIGPIPE in this case? How can I ensure cleanupPipe destructor is always run? When the errorOccurred exception is thrown, I am seeing that cleanupPipe destructor is not run. I have SIGPIPE handler set up to throw an exception, so if SIGPIPE is the result, I would expect my destructor to be run when the SIGPIPE results in thrown exception unwinding the stack.

void
testCase() {
  class cleanup {
  public:
    cleanup(FILE *pipe)
      : _pipe(pipe) {
    }
    ~cleanup() {
      ::pclose(_pipe);
    }

  private:
    FILE *_pipe;

  };

  string cmd("runMyCommandImplementationHere argsHere");
  FILE *pipePtr = ::popen(cmd, "w");
  cleanup cleanUpPipe(pipePtr);

  // Normally, write data to pipe until process in pipe gets all the data it
  // needs and exits gracefully.
  for (;;) {
    if (someErrorOccured()) {
      // When this error occurs, we want to ensure cleanupPipe is run in piped
      // process.
      throw errorOccurred(status);
    }
    if (finishedWritingData()) {
      break;
    }
    writeSomeDataToPipe(pipePtr);
  }
}

void
myCommandImplementationHere() {
  class cleaupPipe {
  public:
    cleanupPipe(const string &filename)
      : _filename(filename) {
    }
    ~cleanupPipe() {
      ::unlink(_filename.c_str());
    }

  private:
    string _filename;

  };

  string file("/tmp/fileToCleanUp");
  cleanupPipe cleanup(file);

  doSomeWorkOnFileWhileReadingPipeTillDone(file);
}
Was it helpful?

Solution

Throwing an exception in a signal handler is a very bad idea. Signal handlers must be asynchronous-safe. To make matters worse, signal handlers run in which is essentially a different thread of execution than your mainline code. It is best to keep your signal handlers small and very primitive. For example, make the SIGPIPE handler set some volatile global variable that indicates that SIGPIPE occurred and test for that as an error condition in your mainline code.

A couple of other comments:

  • You should check the return status when dealing with C functions such as popen, pclose, and write. You aren't doing so on your call to popen or pclose, at least not in the sample code.
  • Why the asymmetry in class Cleanup? The constructor receives an already-constructed FILE pointer, but the destructor destroys it via pclose. IMO it would be better if the constructor calls popen, taking the command string as an argument to the constructor.

Addendum
Perhaps even better than creating a handler for SIGPIPE that sets some global variable is to set the handler for SIGPIPE to ignore, and then check for an EPIPE error from your writes to the pipe.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top