2011-08-07 15 views
1

パイプを使用してコマンドを実行すると、パイプコマンドは何らかのクリーンアップを行う必要がありますが、パイプを開始したプロセスにエラーがある場合、パイプコマンドはクリーンアップされません。この場合、パイプコマンドはSIGPIPEを取得していますか? cleanupPipeデストラクタが常に実行されるようにするにはどうすればよいですか? errorOccurred例外がスローされると、cleanupPipeデストラクタが実行されていないことがわかります。私はSIGPIPEハンドラが例外をスローするように設定しているので、もしSIGPIPEが結果なら、SIGPIPEがスローされた例外のスタックを巻き戻したときにデストラクタが実行されると期待します。popen()edプロセスが終了時にデストラクタを確実に実行する方法を教えてください。

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

スローされた例外をキャッチするものはありますか? –

+0

はい、デイヴィッドの答えがここにあります。シグナルハンドラから例外をスローすることはできず、スタックのunwindが動作することを期待することはできません。 –

+0

@unluddite質問が残っていて、Davidが戻ってこなかったのですが、popen()が開始したプロセスがデストラクタを実行していない理由は何ですか?私の主な問題は、デストラクタが実行され、一時ファイルが削除されることを保証する方法が必要なことです。 – WilliamKF

答えて

3

シグナルハンドラで例外をスローすることは非常に悪い考えです。シグナルハンドラは非同期で安全でなければなりません。さらに悪いことに、シグナルハンドラは本質的にメインラインコードとは異なる実行スレッドで実行されます。シグナルハンドラを小さく、非常に原始的に保つことが最善です。たとえば、SIGPIPEハンドラに、SIGPIPEが発生したことを示す揮発性グローバル変数を設定し、メインラインコードのエラー状態としてテストします。

他のコメントのカップル:

  • あなたは、このようなpopenpcloseとしてC関数を扱うときのリターン・ステータスをチェックし、write必要があります。 popenまたはpcloseへの呼び出しでは、少なくともサンプルコードには記載されていません。
  • class Cleanupのアシンメトリはなぜですか?コンストラクタは既に構築されたFILEポインタを受け取りますが、デストラクタはpcloseによってポインタを破棄します。 IMOコンストラクタがpopenを呼び出し、コンストラクタへの引数としてコマンド文字列を受け取る方が良いでしょう。おそらく、

補遺
いくつかのグローバル変数を設定しますSIGPIPEのハンドラを作成するよりも良いがSIGPIPEを無視するハンドラを設定して、パイプへのあなたの書き込みからEPIPEエラーをチェックするためです。

+0

ああ、私は彼がシグナルハンドラから投げていたことを全く忘れていた。 –

+0

あなたはpclose()が確実にSIGPIPEを引き起こすと言っていますか?私はSIGPIPEを無視しましたが、デストラクタはまだ呼び出されていないので、他のシグナルが起こっていると思います。そうでなければ、デストラクタが実行して一時ファイルを削除すると思います。 – WilliamKF

+0

"あなたはpclose()が確実にSIGPIPEを引き起こすと言っていますか?"いいえ! 'pclose()'を呼び出すと、 'popen()'によって開始されたプロセスへの接続が閉じられ、そのプロセスが終了するのを待ちます。 'pclose()'はあなたのアプリケーションにSIGPIPEを引き起こしません。 SIGPIPEは、 'popen()'によって開始されたプロセスが途中で終了したときに発生します。ここで、「早すぎる」とは、アプリケーションがまだパイプに書き込もうとしていることを意味します。 –

関連する問題