Как обеспечить, чтобы процесс popen()ed запускал деструкторы при выходе?

Если у меня есть канал для запуска какой-либо команды, команда piped должна выполнить некоторую очистку, однако, если процессы, запустившие канал, имеют ошибку, команда piped не очищает. Команда piped получает SIGPIPE в этом случае? Как я могу убедиться, что деструктор cleanupPipe всегда запущен? Когда выдается исключение errorOccurn, я вижу, что деструктор cleanupPipe не запускается. У меня есть обработчик SIGPIPE, настроенный для выдачи исключения, поэтому, если SIGPIPE является результатом, я ожидаю, что мой деструктор будет запущен, когда SIGPIPE приведет к генерируемому исключению, разматывающему стек.

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);
}

1 ответ

Решение

Бросать исключение в обработчик сигнала - очень плохая идея. Обработчики сигналов должны быть асинхронно безопасными. Что еще хуже, работают обработчики сигналов, в которых поток выполнения по сути отличается от вашего основного кода. Лучше всего, чтобы ваши обработчики сигналов были маленькими и очень примитивными. Например, заставьте обработчик SIGPIPE установить некоторую изменчивую глобальную переменную, которая указывает, что произошел SIGPIPE, и проверьте это как условие ошибки в вашем основном коде.

Пара других комментариев:

  • Вы должны проверить статус возврата при работе с функциями C, такими как popen, pclose, а также write, Вы не делаете это по вашему призыву popen или же pcloseпо крайней мере, не в примере кода.
  • Почему асимметрия в class Cleanup? Конструктор получает уже построенный FILE указатель, но деструктор уничтожает его через pclose, ИМО было бы лучше, если конструктор вызывает popen, принимая командную строку в качестве аргумента для конструктора.

добавление
Возможно, даже лучше, чем создание обработчика для SIGPIPE, который устанавливает некоторую глобальную переменную, - установить обработчик для игнорирования SIGPIPE, а затем проверить наличие ошибки EPIPE при записи в канал.

Другие вопросы по тегам