コアでは参照→関数化して参照ゼロを実現

アプリ終了時最後に実行されるCallSetCouplingPtrの入口でアクティブなオブジェクトとして存続しているノードはわずか32個になった.これこそ我が骨の骨,ゼルコバの木のコアシステムそのものだ.これらはすべて「接続」によってリンクされるシステムの骨格であり,ここには「参照」リンクは1つも含まれていない.ZTシステム構成図でこの32個のノードからなる部分図を描いてそれがどう見えるかを試してみよう.

image

かなりわかり易い図になっているが,上図にはまだ欠けている要素がいくつかある.longtable 13個のうち表示されているのは6個だけで,その他にもTITLELINKやTRASHCANなどが未登録だ.かなり難しい問題がぶり返してきているので,データの補充は後回しにして,先に目先の問題を片付けることにしよう.

ZTシステム構成図7.ZELを部分図「コアシステムコンポーネント」基準ノード=1 couplingで開いた後,直系親族図 基準ノード=1 couplingに切り替えようとして,LIST::deleteElement→dataCountDownで停止 DATALIST::findでノードが見つからないというエラー

対象ノードはPAIRBOX#48980でMakePairListClean→ CheckPairList→ …PAIRLIST::Remove→ LIST:_removeで削除されている.対象ノードはノーマルだが,すでにフロート状態になっている.障害はPAIRBOX::MoveSamePointでノード対を共有端点束に移動しようとしているところで起きている.⇒dataCountDownのところでfindするのはそもそも無理なのではないか?外部で削除された場合はデストラクタがoperator deleteより先に呼び出されるから,その時点はまだリストとのコンタクトは続いているが,内部で削除された場合には「事後処理」になるから,当然リストとのコンタクトは切断されていると考えなくてはならない.dataCountDownでは内部/外部を弁別することはできないのだから,find自体が無理と考えるしかない.⇒対処した.これで動作するようになった.

同上サンプルを#201 noduleでソートしようとして,DATALIST:cancel中,PAIRBOX:Dispose→getfamilytreeでgetcouplingが空を返している PAIRLISTはPAIRBOXを直接リスト要素としているはずだが,PAIRBOXの親がLISTELEMENTでその親が空になっている

~PAIRBOXがネストして実行されている.PAIRLIST::cancelでは連続削除ではなく,1点づつ削除しているはずだが…削除対象PAIRBOXのスロットゼロのPAIRBOXは~noduleで前方に繋ぎ替えているはずなのだが… 正確には~nodule→nodl_floatでそれをやっているはずだ.⇒いや,まだ親に繋がったまま~NODULEに入ってくるケースはある.ただし,~NODULEの冒頭でnodl_floatが実行されているから,そこで始末は付いているはずだ.⇒どういうケースで接続したままになっているのかは別途調べる必要がある.

障害が起きているのはEXTRA_ANCHORスロットだ.PAIRBOXはここを端点共有リスト接続に使っている.このスロットはスロットゼロと同様に繋ぎ替え操作が必要だ.現在のdeleteElementにはその操作が入っていない.⇒いや,PAIRLISTのdeleteElementではそれをやっているのでは?⇒PAIRLISTは自前のdeleteElementを持っていない!現行でdeleteElementを持っているのは,DATALISTとLISTだけだ.

PAIRLIST::Removeがノード削除の実行ルーチンだが,これをdeleteElementとdataCountDownに分解するのはかなり難しい.deleteの実行で処理を前後に区分することは可能だが,事前に得た情報が事後に必要になってくる.これをどう伝達することができるか?外部で削除された場合にはそもそもこの事前情報というのは存在しない.必要な措置は,端点共有をどう始末するか?という点だが,これを一般処理の中で実行できるようにする必要がある.EXTRA_ANCHORを使っているのは現時点ではPAIRBOXとMARGBOXだけだが,どちらも接続チェーンとして使っている.EXTRA_ANCHORの接続チェーンの繋ぎ替えをやっている論理をどこかで見た記憶があるのだが…

繋ぎ替えの一般化については後で見ることにして,現在の問題に関する「応急措置」を考えてみることにしよう.cancelで削除されるのだから,これはノーマルな内部処理のケースに該当する.内部処理なのだから,blackflagはOFFで正常に削除されていれば,事後処理で繋ぎ替えが実施されることになるはずだ.cancelでdeleteの代わりにRemoveを使うようにしてこの障害は一応回避できた.ただし,(toplist && !bottomlist())が発生している.bottomlistを空にしているのはPAIRBOX::Disposeから呼び出されているPAIRLIST::CleanSansyoだ.bottomlistに対象ノード対が入っている.

どうも何か大きな勘違いをしていたような気がする.現在実装されているDCD(DataCountDown)の仕組みを整理してみよう.

  1. DCDは接続によって連結された直列リストとその拡張を対象とする
  2. リストクラスは仮想関数deleteElementとdataCountDownを持つ
  3. deleteElementにはそのリストの要素を削除する一般的手順を記述する
  4. deleteElementはそのクラスで実行されるすべてのdeleteをカバーする
  5. detaCountDownには要素を削除した後に実行されるべき後処理を記述
  6. 削除されたオブジェクトのデストラクタでは自ノードの所属するリストを割り出して,detaCountDownを送付する
  7. detaCountDownが実行されたことはblackflagによって識別できる

この仕様にはいくつかの誤認がある.まず,要因が内部であるか外部であるかを問わず,削除されたノードのデストラクタはつねに実行されるから,内部/外部の別なくdataCountDownは(親リストが存続している限り)必ず実行される.従って,blackflagはつねにオンになっているから,フラグとしての意味をなさない.また,dataCountDownが事後に実行されるという観念も誤っている.デストラクタはoperator deleteの実行に先立って実行されるのだから,むしろ事前処理と呼ぶ方が適切だろう.通常の手順では

delete classQ → ~classQ →…→ ~nodule → 切断 → ~NODULE → ゴミ箱/解放  

のようなフローで処理が進行する.オブジェクトの接続を切断してシステム木から切り離す操作が最終ステージ直前の~noduleの段階まで遅延されるのは,親ノードとのコンタクトを失ったノードをコントロールするすべが存在しないためだ.最後までアンダーコントールに置くためには親ノードとの接続が最後まで確保されることが必要である.従って,終末期の接続関係を見て状況を判断するというのは正しくない.

むしろ,blackflagをこれまでと反対の意味(逆論理)で使うことが考えられる.つまり,クラスの内部手続きはそれがノーマルな通常処理であることを認識できるのだから,そこでフラグをONにすることによってオブジェクト側に状態を伝達することができる.オブジェクトのデストラクタではblackflagがONなら通常通り終了し,OFFの場合に限ってdataCountDownを実行する.dataCoutDownは切断の前に実行されるから,まずほとんどのアノーマルケースはこれでカバーできる.

この方式ではこれまでの論理をほとんど修正する必要がないというのが最大のメリットだ.あるクラスの中で実行しているオブジェクト削除を関数化して一箇所に集中するのは意味があるが,deleteElementの実装は必ずしも必須ではない.dataCoutDownで行う処理はほとんどの場合,クラスの内部手続きと共通していると考えられるので,クラス手続き内でこの関数を再利用できればさらによい.今日の修正は大きくないので一度バックアップに戻って出直すことにしよう.

修正は一応完了した.PAIRLISTで(toplist && !bottomlist)というエラーが起きていたが,PAIRBOX::Disposeで実施していたPAIRLIST:CleanSansyoを廃止して動作するようになった.

NAMEBOX::RetrieveGhost→PAIRLIST::remove→nodule::operator deleteで参照カウントが残っている おそらくPAIRLIST:CleanSansyoの廃止とトレードオフになっている ⇒どうもまだ勘違いしているところがあるようだ.operator deleteで参照カウントが残るのは当然で,現在の仕様が,「事後に参照解決する」というものである限り付いて回るだろう.それをCleanSansyoで無理やり解決してきたのが今までの手法で,これは方針を基本的なところで誤っている.

「事後にできることはすべて事前にできる」と考えなくてはならない.人であれば,たとえば,「葬儀」や「相続」などのように死後でなければできないことはある.しかし,オブジェクトにはそんなものは存在しない.もし,オブジェクトが存在しなくなったときの世界がそれまでと少しでも変わるところがあるとすれば,deleteを実行する前にそれを実現してしまえばよい.そうすることの弊害は何もないと考えられる.

最小限必要なことは,「ぎりぎりまで親との接続を確保すること」の一点に尽きる.従って,親との接続関係を切断するときに行わなくてはならないことを除けば,ほとんどの事後処理は事前に実行可能であると考えられる.逆に言うと,deleteの前後で処理を2つの関数に完全に分割するようなことは実際的に不可能である.事後処理のためには事前情報が必要になるため,完全分離することはできない.

そうなると,オブジェクトの削除では事前処理→切断→処分という単純なフローになり,この事前処理の部分はそっくりdataCountDownで実行可能と考えられるから,ノーマルな削除手続きとアノーマルな削除手続きは(親が存続している限り)完全に統合可能であると言える.切断するときの措置に関しては別途考えなくてはならないが,それを除けばこのスキームでまったく問題ないはずだ.別の言い方をすれば,dataCountDownは事後に実行するのではなく,事前に実行されるべきものであると言える.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA