あめだまふぁくとりー

Boost.Graphとかできますん

boost::asio::async_write / read の限界

asio::async_writeasio::async_read に難癖をつけてみます.

stream のコンテキストとハンドラのコンテキストが分離されていない

socket への書き込み (読み込み) 処理は sock_strand 内で実行し, その書き込み (読み込み) 完了のハンドラは handler_strand 内で実行したい場合, どのようにコードを書けばいいでしょうか?

素直に書いてみると以下の様になります.

sock_strand.dispatch([=, &sock]{
    boost::asio::async_write(sock, buffers
            , sock_strand.wrap(handler_strand.wrap(handler));

残念ながらこれは間違いです. 正解はこうなります.

sock_strand.dispatch([=, &sock]{
    boost::asio::async_write(sock, buffers
            , sock_strand.wrap([=](auto const& ec, auto bytes)
    {
        handler_strand.post(bind(handler, ec, bytes));
    }));

前者がダメな理由は async_write / read の内部で使用されるハンドラが最終的に handler_strand 内で呼ばれるため, sock複数のスレッドから操作される可能性があるからです.

asio::async_write / read は対象とする stream とハンドラのコンテキストを上手く分離できません.

なので上記の正解の例でも, ハンドラが呼ばれる際に sock_strand を一度経由するオーバヘッドが発生してしまいます.

内部で例外が起きた場合ハンドラが呼ばれない

asio::async_write / read は内部で複数回 async_write / read_some メンバ関数を呼ぶ可能性があります.

ここで一つ疑問が浮かび上がります.

二回目以降の async_write / read_some メンバ関数の呼び出しで例外が投げられた場合でも, handler は呼ばれるのでしょうか?

実はこの場合, handler は呼ばれません. なのでいくつデータを読み書きしたのかがわからなくなってしまいます.

実際, 例外が投げられそうなのはメモリの allocate に失敗したときぐらいに見えるのであまり心配する必要はないとは思います.

consuming_buffers が Const / MutableBufferSequence の条件を満たさない

consuming_buffers というのは asio::async_write / read の内部で使用されている送信 / 受信データのラッパーです.

Const / MutableBufferSequence::const_iterator は bidirectional iterator でないといけないと, ドキュメントに記載されているのですが, consuming_buffers::const_iterator は forwarding iterator の条件しか満たしていません.

そもそもなんで bidirectional iterator が要求されているんでしょう?