かそくそうち このページをアンテナに追加 RSSフィード

2007-07-09

[]ミニマルHTTPサーバ 20:56 ミニマルなHTTPサーバを含むブックマーク ミニマルなHTTPサーバのブックマークコメント

http://0xcc.net/blog/archives/000178.html

このコードには長時間動作させる上で明らかにマズい点が二つあります。

(22:58追記: 問題のコードは既に修正されています。高林さん、すばやい対応ありがとうございます。)

UNIXネットワークプログラミング」の別の節に解説はありますが、コードだけコピペした人が困らないよう指摘しておきます。


一つ目はSIGPIPEシグナルの発生に備えていない点です。

サーバーがレスポンスを返す前にクライアントがソケットを閉じてしまうと、write()がSIGPIPEシグナルを生成することがあります。

SIGPIPEの既定の動作はプロセスの終了なので、この状況が発生しただけでサーバーは勝手に(coreを残さず)終了してしまいます。

通常は次のような関数でSIGPIPEを無視します。

#include <cstring>
#include <signal.h>

void ignore_sigpipe()
{
    struct sigaction act;

    // 処理系依存の追加メンバに備え、ゼロクリア
    std::memset(&act, 0, sizeof(act));

    act.sa_handler = SIG_IGN;
    sigemptyset(&act.sa_mask);

    if (::sigaction(SIGPIPE, &act, 0) == -1)
        handle_error(); // 適当なエラー処理
}

この結果、write()がSIGPIPEで失敗した場合にerrnoにEPIPEがセットさせるようになります。

UNIXネットワークプログラミング」では5.13節に解説があります。


二つ目はaccept()のエラー処理に関してです。

accept()はよくエラーを返す関数なので、エラー即終了というのはよくありません。

  • TCPのハンドシェイクが完了してaccept()が呼ばれるまでの間にコネクションが切断された場合、errnoにECONNABORTEDがセットされる
  • プロセスがシグナルを受信しaccept()が中断された場合、errnoにEINTRがセットされる

後者はaccept()に限った話ではないので重箱の隅ですが、EINVALのような明らかなプログラミングエラーを除いて、accept()がエラーを返してもリトライする必要があります。

また、環境によっては別のエラーコードがセットされる場合もあります。

http://www.linux.or.jp/JM/html/LDP_man-pages/man2/accept.2.html

UNIXネットワークプログラミング」では5.11節に解説があります。


まとめ

[][]exec.register-exec-all 23:30 exec.register-exec-allを含むブックマーク exec.register-exec-allのブックマークコメント

execルールをさらに改良しました。

exec.jamの差分

execルールの目的は、

  • ユニットテストとは関係なく単に実行したい
  • 実行の成否に関わらず、毎回明示的に実行したい
  • bjamで生成したDLLへのPATHを自動で設定したい

なので、予め決まったラウンチャや引数で実行するよりもコマンドラインから指定できたほうが便利だと考えました。

そこで、これらの設定をフィーチャーで指定するのをやめて--debuggerオプションと--argsオプションで指定できるように変更しました。

これで、

import exec ;
exe hoge : hoge.cpp ;
exec hoge-run : hoge ;

というJamfileを書いておくと、

# gdbからhogeを実行
bjam hoge-run --debugger=gdb

とか、

# hogeに引数「1.txt 2.txt」を指定して実行
bjam hoge-run --args="1.txt 2.txt"

といったことが可能になります。


おまけで、カレントプロジェクトのexeすべてにexecを呼び出すregister-exec-allルールも用意しました。

rule register-exec-all ( )
{
    import "class" ;
    import project ;

    local proj = [ project.current ] ;

    # アクセッサがないので、self.alternativesメンバを覗き見
    local targets = [ modules.peek $(proj) : self.alternatives ] ;

    for t in $(targets)
    {
        if [ class.is-a $(t) : typed-target ] && [ $(t).type ] = EXE
        {
            local name = [ $(t).name ] ;
            exec $(name)-run : $(name) ;
        }
    }
}

これをJamfileの末尾で呼んでおけば「exeターゲット名-run」という名前でexecターゲットが作成されます。

早速Hamigakiライブラリの各exampleディレクトリで使うようにしてみました。


これでbjam_winを作り始めた動機は全て解決できたので、そろそろ別の話題に移りたいと思います。bjam_winは中途半端ですが。

トラックバック - http://d.hatena.ne.jp/y-hamigaki/20070709