NDKPI オブジェクト有効期間要件

NDK オブジェクトの作成、使用、クローズ方法

NDK コンシューマーは、そのオブジェクトの NDK プロバイダーの作成関数を呼び出すことによって、NDK オブジェクトの作成要求を開始します。

コンシューマーが 作成関数を呼び出すと、NdkCreateCompletion (NDK_FN_CREATE_COMPLETION) をパラメーターとして渡します。

コンシューマーは、オブジェクトのディスパッチテーブルでプロバイダー関数を呼び出し、NdkRequestCompletion (NDK_FN_REQUEST_COMPLETION) 完了コールバックをパラメーターとして渡すことにより、さまざまな要求を開始します。

オブジェクトが不要になると、コンシューマーはプロバイダーの NdkCloseObject (NDK_FN_CLOSE_OBJECT) 関数を呼び出してオブジェクトのクローズ要求を開始し、NdkCloseCompletion (NDK_FN_CLOSE_COMPLETION) コールバックをパラメーターとして渡します。

プロバイダーは、コンシューマーのコールバック関数を呼び出して、要求を非同期的に完了させます。 この呼び出しは、プロバイダーが操作を完了し (オブジェクトを閉じるなど)、コンシューマーに制御を返していることをコンシューマーに示します。 プロバイダーが同期的にクローズ要求を完了した場合、成功した場合でもエラーになった場合でも、コンシューマーのコールバック関数は呼び出されません。

完了コールバックのルール

プロバイダーがコンシューマーの要求でオブジェクトを作成すると、プロバイダーはコンシューマーの NdkCreateCompletion コールバックを呼び出して、オブジェクトが使用できる状態であることを示します。

コンシューマーは、最初のコールバックが返されるのを待たずに、同じオブジェクトに対して他のプロバイダー関数を呼び出すことができます。

コンシューマーは、そのオブジェクトのすべてのプロバイダー関数が返されるまで、オブジェクトの NdkCloseObject 関数を呼び出しません。

ただし、プロバイダー関数が完了要求を開始した場合、プロバイダー関数が返されていなくても、コンシューマーはその完了コールバック内から NdkCloseObject を自由に呼び出します。

プロバイダー関数は、以下のいずれかの操作を行うことで、コールバックから戻る前に完了要求を開始することができます。

  • 完了コールバックを直接呼び出す
  • 完了要求を別のスレッドにキューイングする

完了要求を開始することで、プロバイダーは実質的にコンシューマーに制御を返します。 プロバイダーは、プロバイダーが完了要求を開始した後、いつでもオブジェクトを閉じることができると仮定する必要があります。

: 完了要求を開始した後にデッドロックを防ぐには、プロバイダーは以下のいずれかを行う必要があります。

  • 完了コールバックが返されるまで、オブジェクトに対して他の操作を実行しないでください。
  • プロバイダーがオブジェクトに絶対に触れる必要がある場合は、オブジェクトをそのまま維持するために必要な対策を講じてください。

例: コンシューマーとプロバイダーの相互作用

以下のシナリオについて考えてみます。

  1. コンシューマーはコネクタ (NDK_CONNECTOR) を作成し、それからNdkConnect (NDK_FN_CONNECT)を呼び出します。
  2. プロバイダーは接続要求を処理し、エラーにヒットして、NdkConnect 呼び出しのコンテキストでコンシューマーの完了コールバックを呼び出します (内部実行の選択によるインラインエラーの返しとは対照的)。
  3. コンシューマーは、NdkConnect 呼び出しがまだコンシューマーに返されていない場合でも、この完了コールバックのコンテキストで NdkCloseObject を呼び出します。

デッドロックを回避するには、手順 2 の後にプロバイダーがコネクタオブジェクトに触れないようにする必要があります (NdkConnect 呼び出し内で完了コールバックを開始したポイント)。

先行オブジェクトと後続オブジェクトを閉じる

コンシューマーが後続オブジェクトに対して NdkCloseObject を呼び出す前に、NdkCloseObject 関数を呼び出して先行オブジェクトを閉じるには、プロバイダーを準備する必要があります。 コンシューマーがこれを行う場合、プロバイダーは次のことを行う必要があります。

  • プロバイダーは、すべての後続オブジェクトが閉じられるまで、先行オブジェクトを閉じてはなりません。つまり、すべての後続オブジェクトが閉じられると、プロバイダーはクローズ要求から STATUS_PENDING を返して (クローズ要求に対して登録されている NdkCloseCompletion 関数を呼び出して)それを完了する必要があります。
  • コンシューマーは、NdkCloseObject を呼び出した後に先行オブジェクトを使用しないため、プロバイダーは、先行オブジェクトに対してそれ以上プロバイダー関数を失敗させるための処理を追加する必要はありません (ただし、選択する場合は可能です)。
  • プロバイダーは、必要な場合を除き、最後の後続オブジェクトが閉じられるまで他の副作用を持たない単純な逆参照のようにクローズ要求を処理できます (必要な副作用がある以下の NDK リスナーのクローズケースを参照してください)。

後続オブジェクトの進行中のクローズ完了コールバックがプロバイダーに戻る前に、プロバイダーは先行オブジェクトに対するクローズ要求 (NDK_ADAPTER クローズ要求を含む) を完了してはなりません。 これは、NDK コンシューマーが安全にアンロードできるようにするためです。

NDK コンシューマーは、コンシューマーコールバック関数内から NDK_ADAPTER オブジェクト (ブロック呼び出し) に対して NdkCloseObject を呼び出しません。

アダプターオブジェクトを閉じる

以下のシナリオについて考えてみます。

  1. コンシューマーは、完了キュー (CQ) オブジェクトで NdkCloseObject を呼び出します。
  2. プロバイダーは STATUS_PENDING を返し、後でコンシューマーの完了コールバックを呼び出します。
  3. この完了コールバック内で、コンシューマーは、NDK_ADAPTER を閉じても OK であることをイベントに通知します。
  4. 別のスレッドがこのシグナルを起動し、NDK_ADAPTER を閉じてアンロードに進みます。
  5. ただし、コンシューマーの CQ クローズ完了コールバックが呼び出されたスレッドは、まだコンシューマーのコールバック関数 (関数エピローグなど) 内にある可能性があるため、コンシューマードライバーがアンロードするのは安全ではありません。
  6. 完了コールバックコンテキストはコンシューマーがイベントを通知できる唯一のコンテキストであるため、コンシューマードライバーは安全なアンロードの問題自体を解決することができません。

コンシューマーがすべてのコールバックが制御を返したことを保証できるポイントが必要です。 NDKPI では、NDK_ADAPTER のクローズ要求が制御を返すときがこのポイントです。 NDK_ADAPTER クローズ要求はブロック呼び出しであることに注意してください。 NDK_ADAPTER クローズ要求が返されると、その NDK_ADAPTER オブジェクトから派生するすべてのオブジェクトのすべてのコールバックが、プロバイダーに制御を返したことが保証されます。

クローズ要求の完了

プロバイダーは、以下の場合までオブジェクトに対するクローズ要求を完了してはなりません。

  • オブジェクトに対するすべての保留中の非同期要求が完了するまで (つまり、完了コールバックがプロバイダーに返されるまで)。
  • コンシューマーのすべてのイベント コールバック (たとえば、CQ の NdkCqNotificationCallback (NDK_FN_CQ_NOTIFICATION_CALLBACK)、リスナーの NdkConnectEventCallback(NDK_FN_CONNECT_EVENT_CALLBACK) がプロバイダーに返されるまで。

プロバイダーは、クローズ完了コールバックが呼び出された後、またはクローズ要求が STATUS_SUCCESS を返した後に、それ以上コールバックが行われないことを保証する必要があります。 クローズ要求では、保留中の非同期要求のフラッシュまたは取り消しが必要な場合にも開始する必要があることに注意してください。

注意: 上記から論理的に導かれることは、NDK コンシューマーは、NDK_ADAPTER オブジェクトに対して、コンシューマーのコールバック関数内から NdkCloseObject を呼び出してはならないということです(これはブロック呼び出しです)。

Network Direct Kernel Provider Interface (NDKPI)