mike-neckのブログ

Java or Groovy or Swift or Golang

libcurl の curl_multi_socket_action のメモ

Swift の URLSession の中で呼び出されている curl_multi_socket_action の動作を調べていたので、そのメモ。

f:id:mike_neck:20180609045321p:plain

基本的にはドキュメントを自分向けに意訳しただけ。

curl_multi_socket_action

  1. multi handle を作る
    • URLSession._MultiHandlerawHandleCFURLSessionMultiHandleInit で初期化する箇所に該当する
  2. socket コールバックを CURLMOPT_SOCKETFUNCTION オプションと共に curl_multi_setop で登録する。これによって、 multi handle の socket_cb に指定したコールバックが登録される。
    • URLSession._MultiHandler では setupCallbacks() 内での CFURLSession_multi_setopt_sf 呼び出しに相当する。
  3. タイムアウトコールバックを CURLMOPT_TIMERFUNCTION オプションと共に curl_multi_setop で登録する。このタイマーファンクションは timeout_ms (というコールバックのパラメーター)が変更されたときに呼び出される。タイムアウトハンドラーに求められるのは 1. ループしないこと、 2. curl_multi_socket_action または curlmulti_perform を呼び出すこと、 3. timeout_ms-1 の場合はタイマーの削除/0 の場合は curl_multi_socket_action または curl_multi_perform の呼び出しを行うことの3つである
    • URLSession._MultiHandler では setupCallbacks() 内での CFURLSession_multi_setopt_tf 呼び出しに相当する。
    • 渡された timeout_ms をもって、 _MultiHandle 内の timeoutSourceタイムアウトを設定する
      • -10 の場合は timeoutSource は削除される
      • 0 の場合は timeoutTimerFired が呼び出す
      • その他の timeout_ms の場合は timeoutSourcetimeoutTimerFired を呼び出すクロージャーが設定される。このクロージャーは URLSession が保持する DispatchQueue をベースに作られた DispatchSourcetimeout_ms ミリ秒ごとに起動される(timeout_ms ミリ秒遅延して開始)
  4. easy handle を multi handle に追加する(curl_multi_add_handle)
    • URLSessionTask にて resume 関数が呼び出された際に、 URLProtocol の内部状態を表す internalState が更新される
    • その setter にて URLSessionadd(handle:) 関数に _EasyHandle が渡されて、 curl_multi_add_handle が呼び出される
  5. libcurl が使っている ソケットを何らかの方法で管理する。 一般的には gliblibevent を使うらしい。
  6. curl_multi_socket_action(渡すソケットは CURL_SOCKET_TIMEOUT 定数) を呼び出すと、コールバックの呼び出しが始まるようになる
    • URLSession の場合は 3. にて DispatchSource に設定したクロージャーが呼び出されることにより始まる(と思われる)
  7. イベントの発生を待つ
  8. イベントが発生した場合は、イベントの発生した socket に対して curl_multi_socket_action を呼び出す。タイムアウトが発生した場合は 6. から繰り返す。
    • これは 2. にて渡した socket コールバックが register を呼び出すことによって実現される。
    • URLSession の保持する DispatchQueue をベースにつくった SocketSource 上でディスクリプターからの読み込み/書き込み可能イベントを待ち合わせる
    • ↑ のイベントハンドラーにて URLSession._MultiHandleperformAction が呼び出され、 ディスクリプターからの読み書きが行われる

中途半端だけど、寝る時間になったので終わり