トップ «前の日記(2017-12-06) 最新 次の日記(2017-12-08)» 編集

Orz日記 by Akio Morita

ToDo:

  • 15 SAD Fit[]回りの障害事例の解析
  • 10 smart pointer版PEGクラスの再実装(Left Recursionまわり)
2006|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|06|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|07|08|09|10|11|12|
2013|01|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|06|07|08|10|12|
2016|01|02|03|05|06|08|10|11|
2017|01|02|03|04|05|06|07|09|10|11|12|
2018|01|02|03|04|06|07|08|09|10|11|12|
2019|01|03|04|05|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|

2017-12-06 [長年日記]

_ [SAD]CaMonitorオーバーホール

  • EPICS\$CaPut, EPICS\$CaPutCBの再実装完了
    • 引数型 Real/Stringと ca_field_typeが整合しないケースは、旧実装に合わせてあるが、もっと適切な処理が有るか?
      • Real引数に対して DBF_STRING fieldの場合、DBR_DOUBLEで送信
      • String引数に対して DBF_STRING以外のfieldの場合も、DBR_STRINGで送信

evidテーブルリソースリークへの追加の考察

  • ca_evid_to_chid APIが使えるには、evidが指すクラスオブジェクトが生存している必要が有る
  • ca_clear_channel APIで、chidに紐付けされたevidオブジェクトが破壊されると、API呼び出し後には evidchid変換出来ない
    • ca_clear_channel呼び出し前に、evidテーブルのスキャンを行うもしくは、evidテーブル側に紐付けされているchidを格納しておく必要が有る
    • EPICS\$CaAddEvent(ca_create_subscription)で生成されるevidに紐付けされるchidは唯一なので、evidテーブルへの追加情報は固定長(ポインタ)で良い
    • chidの結びついたevid数が未知の場合、evidテーブルの全エントリをスキャンする必要が有る
    • ca_clear_channel前にevidを開放する戦略の場合、ca_clear_channelの呼び出しが失敗するケースを想定する必要は?
      • ca_clear_channelの失敗時の返り値は、ECA_BADCHIDのみ
      • 従って、失敗するケースはchidテーブルの登録情報が破損している or 何らかのframework外からの操作でオブジェクトが破壊されているケースなので、考慮する必要はないと思われる

従って、以下の方針で修正する

  • chidテーブルに、evidテーブルからの参照数を記録する
  • chidテーブル経由でca_clear_channelする際に、evidテーブルからの参照が残っている場合は、evidテーブルをスキャンしてca_clear_subscriptionを行う
    • ca_clear_channelの呼び出しは、CaClose1の実装側からもあることに注意
      • 最終的には、CaOpen1/'CaOpen2/CaClose1のバックエンド実装をEPICS\$CaOpen/EPICS\$CaClearChannelと統合すべきである
        • 扱い的には、EPICS\$CaPut系と同様にCallbackフラグ付きの内部インターフェースを設ける?
  • CaMonitorのDestructorでは、Stopメソッド(EPICS\$CaClearEvent)を実行してからEPICS\$CaClearChannelする
    • evidテーブルの線形検索時間を短縮するため

_ [SAD]CaOpen1/CaOpen2系の動作

CaOpen系とCaClose系のコードは、EPICS\CaOpenとEPICS\CaClearChannelに統合できそうなので、動作仕様の違いをまとめておく

ca_create_channel系(CaOpen1/CaOpen2)

関数引数の数callbackca_pend_ioretrychidリーク
CaOpen11なしありなしca_pend_ioタイムアウト時
CaOpen21 or moreなしありありca_pend_ioタイムアウト・リトライ時
EPICS\$CaOpen1ありなしなしなし
  • CaOpen系は、ca_pend_ioレベルでタイムアウトする(レコードが規定時間以内に接続しない)場合にMessageを返すため、ca_create_channelしたchid構造体が破壊されない
  • 特に、CaOpen2では、リトライ動作があるために存在しないレコードに対するアクセスで、複数個のchidがリークする
    • 引数中にエラーとなるレコードがあると正常に接続できたレコードのchidも複数個生産・リークする
  • 実装統合にあたり
    • chidのリークは直すべきバグと思われる
    • タイムアウトしきい値とリトライ動作に関しては要検討

ca_clear_channel系(CaClose1)

関数引数の数ca_pend_io
CaClose11あり
EPICS\$CaClearChannel1なし
  • CaClose1のca_pend_ioにリトライコードがあるが、リトライする必要があるのか不明
    • =ca_clear_channel時点で、当該chidのデストラクタ呼び出し要求は通っているはず=
    • =このケースでの、タイムアウトの長い ca_pend_ioと短い ca_pend_ioの複数回呼び出しの違いが分からない=
    • ca_clear_channel要求は、ca_flush_ioで送出されるので、ca_pend_ioは不要(2017/12/11追記)

ca_pend_io系(CaPendIO)

  • EPICS\$CaPendIOとエラー時に返すMessageを除いて機能は同一
  • なぜか、短い時間のca_pend_ioを繰り返す実装である
    • ca_pend_ioはタイムアウト時間が長くても、outstanding taskが完了した時点で戻ってくる上に、当該ループで別のイベントハンドラを回すわけでは無いので、必要性が理解できない
    • 単純に、EPICS\$CaPendIOの別名で良いと思われる
    • ハンドラ無しのca_create_channel/ca_get要求が存在しない場合、timeoutが可変な ca_pend_ioを回す必要性が不明(2017/12/11追記)

ca_state系(CaConStatus1)

  • CaMonitor backendには、CaConStatus1の対応物は無い
    • chid構造体の参照動作のみなので、再実装に簡単

ca_get系(CaRead1/CaRead2)

  • CaMonitor backendには、CaRead1/CaRead2の対応物は無い

ca_put系(CaWrite1/CaWrite2)

  • 機能的には、CaMonitor@Put/PutCB相当だが、ca_pend_ioベースのblockingアクセスとなる
  • CaWrite2に関しては、chidとvalueのペアを複数引き受けて、ca_pend_ioによるblockingを一回ですませるmassitve ca_putインターフェースがある

未実装系(src/tfefun2.f)

  • CaChName
    • シンボル登録あり・実装不在
    • 名称から、chid構造体のca_nameによる参照を取ってくるのを期待していると推定される
    • 実装フローは、CaConStatus1と同一(返り値の型が異なる Real vs String)
  • CaStatus2
    • シンボル登録自体がコメントアウトされている
    • CaOpen2系用ca_state参照を意図していたのか?

_ [SAD]CaOpen1/CaOpen2の返り値

調査結果のまとめ

CaOpen1[record]

実装は、nfCaOpen @ src/tfefun2.f (casearch_/capendio_)

  • 戻り値 Real (chidハンドル)
  • エラー時のMessage
    • ca_create_channel - CA::search
    • ca_pend_io - CA:Channel open time out

CaOpen2[record...]

実装は、tfefun2 @ src/tfefun2.f (casearch_/capendio_)

  • 戻り値 {Real...} (chidハンドルのリスト)
  • エラー時のMessage
    • ca_create_channel - CA::open
    • ca_pend_io - CA::open
  • リトライ動作
    • 最大2回(計3回)
    • ca_pend_ioの基本タイムアウト - 0.5~4.0秒 Restrict[0.2 * narg, 0.5, 4.0]
    • リトライ毎に 1.414倍になる
      • 最大リトライ時で、合計 4.413倍
      • 最大待ち時間 17.65秒
引数基本タイムアウト
10.5
20.5
30.6
40.8
81.6
122.4
163.2
204.0
244.0

再実装に関する考察

  • CA::searchをCA::openに統合すべきと思われる
    • CaOpen[] wrapperは、CaOpen2[]に依存しているので、CaOpen1[]の仕様変更はインパクトが小さいはず
  • CaOpen2[]のエラー処理は、どうするべきか?
    • Messageを返す場合は、chidハンドルを返せないので、ca_clear_channelで解体すべきである(リソース管理的に)
    • リトライを実装する場合、接続に失敗しているレコードのみ再要求すべきである
    • CaOpen[] weapperから複数レコードの解決を要求されたケースでは、解決できた分だけでも返すべきでは?
  • CaOpen2[]の返り値は、Real Listなので、itavalocで直接構築すべき
  • CaOpen wrapper(Packages/CaSad.n)では、CaOpen2[]はString型に対する呼び出しのみ

_ [SAD][EPICS]ca_create_channelのエラー

レコード名に長い文字列を与えて、ca_create_channelがECA_BADSTRを返す状況にすると、SEVCHKマクロでSEGVする

当該エラーは、nciu::nciu(コンストラクタ)内のnameLengthTempチェックからthrowされるcacChannel::badString例外に起因している

レコード名の長さがMAX_UDP_SEND - sizeof(caHdr)を超過している際に発生し、libca内の構造体の整合性が破壊されている模様(試験環境でのしきい値は、1008bytes)

ca_create_channelに投げる前に長さをチェックすべき

2017/12/08追記

  • MAX_UDP_SEND - caProto.hにて 1024 octetに設定されている
  • caHdr型 - caProto.hca_hdr構造体 uint16 x4 + uint32 x2なのでパディング無しで 16byte
  • nameLengthTemp - strlen(pNameIn) + 1なので、NULL文字を含むレコード名の長さ

従って、1024 - 16 → 1008文字のレコード名(NULL文字を除く)で cacChannel::badString例外が発生する


カテゴリー: Admin | Emacs | EPICS | Fortran | FreeBSD | GCC | hgsubversion | IPv6 | KEKB | LHC | Lisp | LLVM | MADX | Ryzen | SAD | samba | tDiary | unix | WWW | YaSAI | お仕事 | イベント | 出張 | 宴会 | 数学 | 艦これ | 買いもの | 追記 | 雑記