1

プロパティは次のように記述できます。アクションがキャンセルされた場合、そのハンドラは error で実行されることが保証されます

たとえば、boost::asio::deadline_timerには、deadline_timer::cancel 関数のドキュメントの備考セクションで説明されているように、このプロパティがありません。そのため、タイマーの待機操作をキャンセルしても、そのコールバックがエラーなしで実行される可能性があります。

一方、このプロパティは asio ソケットにも当てはまります (少なくとも私はそう願っています:) ドキュメントにはそのようなコメントはありません)。

編集: デッドライン タイマーにこのプロパティがないことを示す疑似コード:

1# User calls timer.async_wait with a handler H which is to be
   executed when the action finishes.
2# Time passes.
3# Timeout has been reached, asio internally inserts the handler H into
   a queue for later execution, but with error code indicating success.
   User is unaware of when this step takes place.
4# User calls cancel on the timer, thus would expect the handler to be
   executed with an error code indicating failure.
5# Asio takes the handler H from the queue and executes it with error
   code indicating success as set in the step #3.

この問題は、手順 4 でブール値フラグを設定し、手順 5 で確認するだけで簡単に回避できるため、問題にはなりません。

4

1 に答える 1

4

ドキュメントNetworking Library Proposal for TR2も、キャンセル時にエラーが保証される非同期操作の用語を定義していません。ただし、 を含むすべての非同期操作は、boost::asio::deadline_timer::async_wait()この動作を示します。ハンドラは、関連付けられた操作のステータスを常に提供するように設計されています。この保証がないと、キャンセルまたは複数の操作が発生したときに、開発者はハンドラー内で不明な状態になります。

キャンセルは、まだ実行されていない操作に対してのみ機能します。非同期操作の可視性が異なるため、ドキュメントではタイマー クラスについてのみこれが強調されていると思います。I/O オブジェクトの操作は可視性が高くなります。たとえば、ネットワークをスニッフィングして、ソケットでの非同期書き込み操作を観察できます。一方、タイマーの操作は可視性が低いです。待機操作のメカニズムは Boost.Asio 内の実装の詳細であり、API は操作の外部監視を実行する機能を提供しません。

擬似コードでは、タイムアウトに達しており、非同期待機操作が完了したことを示しています。そのため、アクションは既に実行されているため、キャンセルできなくなります。したがって、ハンドラは のエラーで呼び出されませんboost::asio::error::operation_aborted。キャンセルはアクションであり、状態の変更ではないことを理解することが重要です。したがって、タイマーを照会してキャンセルが発生したかどうかを確認する方法はありません。また、キャンセルによってハンドラーのエラー コードが変更されることをユーザーが予期している場合、ユーザーは、非同期操作の開始、完了、および通知の間の時間の分離に起因する継承の複雑さに戸惑う可能性があります。


以下のすべては、実装の詳細に非常に具体的です。このシナリオでは、Boost.Asio がリアクターに epoll を使用するシステムで、boost::asio::ip::tcp::socket::async_receiveを使用してソケットで非同期読み取りが行われます。

初期化

  1. ソケット オブジェクトが作成されると、そのサービスreactive_socket_service_baseはリアクタにタスクを初期化するように通知します( reactor::init_task())。
  2. 次に、リアクターはio_serviceにタスクの初期化を通知します。これにより、マーカー操作がio_serviceの操作キューに追加されます。
  3. ソケットが有効なファイル ディスクリプタで開くと、ファイル ディスクリプタに関連付けられたデータの構造体( descriptor_data)がリアクタに登録されます。この構造体には独自の操作キューがあり、実際には操作自体として扱われます。
  4. リアクタはファイル記述子を抽出し、ファイル記述子descriptor_dataのリストに追加して、リアクタ内で観察します。

非同期読み取り操作の開始

  1. boost::asio::detail::reactive_socket_service_base::async_receive()オブジェクトを作成しboost::asio::detail::reactive_socket_recv_opます。このオブジェクトは操作オブジェクトです。そのperform()メンバー関数はソケットからデータを読み取ろうとし、そのcomplete()メンバー関数はユーザーの完了ハンドラーを呼び出します。
  2. boost::asio::detail::reactive_socket_service_base::start_op()を呼び出す読み取り操作で呼び出されますreactor::start_op()
  3. リアクターdescriptor_dataは、ソケットの を取得し、記述子固有のミューテックスをロックし、操作を記述子固有の操作キューにプッシュし、作業があることを通知io_serviceしてから、ミューテックスをロック解除します。

ソケットがデータを取得し、非同期読み取り操作の完了が開始されます

  1. io_service::run*()が呼び出されます。
  2. io_service、操作の実行準備ができている場合にのみ、その操作キューをチェックします。この場合、初期化中に作成されたマーカー操作はキューにあります。操作は、リアクタが存在することを示すマーカーとして識別され、 を呼び出しますreactor::run()
  3. リアクターは でブロックされepoll_waitます。ソケットのファイル記述子にアクティビティがあると、イベントが識別されます。はdescriptor_dataイベントから抽出され、操作と同様に呼び出し元の操作キューにプッシュされますdescriptor_data
  4. 呼び出し元に戻された操作は、io_serviceの操作キューに追加されます。このキューには、descriptor_data操作と元のマーカー操作が含まれています。
  5. descriptor_data操作がキューからポップされ、メンバー関数io_serviceが呼び出されて実行されます。complete()epoll_reactor::descriptor_state::do_complete
  6. do_completeの操作キュー内の操作が繰り返され、epoll_reactor::descriptor_state::perform_io呼び出されます。これには、非同期操作の開始中にキューにプッシュされた操作が含まれます。descriptor_dataperform()boost::asio::detail::reactive_socket_recv_op
  7. 読み取り操作のperform()メンバー関数が呼び出さsocket_ops::non_blocking_recv()れ、実際のデータがソケットから読み取られようとします。転送されたエラー コードとバイト数は、オペレーションに格納されます。
  8. オペレーションはdescriptor_dataのオペレーション キューから削除され、io_serviceビアに追加されtask_io_service::post_deferred_completionます。

非同期読み取り操作の通知

  1. io_serviceキューには、完了した読み取り操作とマーカー操作の 2 つの操作があります。のreactorキューにはオペレーションがありません。
  2. 読み取り操作は最終的に繰り返され、そのcomplete()メンバー関数が呼び出されます。内reactive_socket_recv_op::do_completeで、ユーザー ハンドラは、エラー コードと転送されたバイトを使用して呼び出します。

キャンセル

  1. boost::asio::detail::reactive_socket_service_base::cancel()reactor::cancel_opsで呼び出しますdescriptor_data
  2. epoll_reactor::cancel_ops()descriptor_dataの操作キューを反復処理します。各操作はdescriptor_dataの操作キューから取り出され、エラー コードが に設定されてboost::asio::error::operation_abortedから、 の操作キューに追加されio_serviceます。

descriptor_dataしたがって、キャンセルは、の操作キューから削除することによって、まだ呼び出されていない操作にのみ影響します。オペレーションが呼び出された場合、そのオペレーションはすでにdescriptor_dataのオペレーション キューから削除され、オペレーション キューに追加されていio_serviceます。

于 2013-01-02T01:57:30.883 に答える