「完全な被参照リスト」を整備する

「完全な被参照リスト」を整備するというのは積年の懸案だった.実装をためらっていたのは主に(時間的・空間的)効率上の理由からだが,CPUがここまで速くなりRAMの実装も4~8GBといういまの時代にあってはためらうべき何の理由もない.完全参照リストが完備するとこれまでのシステムメンテナンス上の苦労の過半が解消することになり,システムは思い切りシンプルなものになることだろう.とは言え,これによって参照管理が完全に自動化されるというものでもない.オブジェクトがランタイムで削除されるときシステムの恒常性を維持するためにはそれなりのことは(いままで通り)実行されなくてはならない.そこまで自動化できればプログラミングの苦労も相当軽減されて,本質的な論理構築に専念できるようになるのだが…

それでも完全な参照リストがあれば,そのオブジェクトを参照しているノードを完全に把握できるので,そのノードのクラスと参照の置かれたスロット番号が分かれば状況はかなり改善できるはずだ.この意味では完全参照リストに記載する情報にスロット番号を追記しておくことは意味があるかもしれない.そうすれば,完全参照リストは真の意味で完全なものになるとも言える.これに要するコストはそれほど大きくはないので実装すべきだろう.すでにnoduleクラスにはReferenceListという単体オブジェクトを対象とする参照リスト管理が備わっている.これはデバッグ支援を目的としているが,対象オブジェクトを全ノードに拡大すればそのまま完全参照リストシステムになり得るものだ.

すでに基盤作りは完了しているのであとはReferenceListを使う論理の拡張を行うだけになっている.修正は「完全被参照リスト管理の整備@20201104」とする.⇒とりあえず,ReferenceListをそのまま使って対象を全ノードに拡張する修正を試してみる.⇒この関数はNODULE::xpandを使うように特化されているので,別建てにした方がよい.⇒nodule::ReferenceControlとした.⇒実装した.

一発でスタックオーバーフローしてしまった.原理的に無理なことをやっているのか?単純なミスなのか?それが問題だ.LISTノードを再帰的に生成しようとしている.参照リストはNODULEのreferistスロットに接続されるだけなので参照が発生する可能性はないと思っていたのだが,LISTは参照末尾を押さえるためにbottomという参照リンクを使っている…LISTはDATALISTの派生クラスだが,bottomはDATALISTですでに定義されている.bottomを持たなくてもリスト管理できない訳ではないが,末尾を押さえていれば要素を追加するときの便宜がある.

DATALISTは抽象クラスとみなされるので,bottomをLISTで管理するようにして,参照リストはDATALISTから直接派生するようにしたらよいのではないか?⇒それしかないような気がする.どうも結構大きな修正になってきた.DATALISTから直接派生しているのはLISTしかなかったと思うが,システム図で確認しておこう.⇒確かにそのようだ.

DATALISTからbottomを除去するのは簡単にできた.あとは,DATALISTで除去した分をLISTに移すだけだ.⇒完了した.スタックオーバーフローは発生しなくなったが,参照カウントの不一致が発生している.これを見る前に参照リストをDATALISTから直接派生しなくてはならない.⇒これはnewステートメントを書き換えるだけで済むが,LISTの関数を使っているので,それを整備しなくてはならない.findlinkとremoveだ.この2つは逆にDATALISTに移入してやることにしよう.この2つの関数はLISTにも残しておく必要がある.findlist,precedingも必要.⇒LISTに残しておくべきいくつかの関数をDATALISTで直接書き換えてしまった.⇒バックアップから復元した.

ようやく動き始めたが,どこか壊してしまったようだ.TOPOLOGY:FindJikusenAncestorで枝グラフの操作中にエラーが出ている.グラフの枝リストはLISTの派生クラスであるNLISTだが,toplistが存在しているのにbottomlistが空になっている.いや,リスト自体は健全だ.datacount=0でtoplist,bottomlistともに空になっている.エラーが発生しているのはputを連結する位置のノードをthisで初期化しているためだ.⇒違うところを見ていた.テンプレートクラスの関数はクラス内で定義されていてもシグネーチャごとに実装されているので,動いているのは別ものだ.確かにtoplistが非空でbottomが空という状態になっている.datacountはゼロなのにtoplistが空になっていない.

toplistはDATALISTが設定しているので,これはDATALISTの論理の誤りと思われる.おかしい.LIST::_removeでもtoplistを更新している場所が見えない.リスト要素はtoplistに接続しているので明示的にリンクしなくてもdeleteなどのアクションで自動的に更新されるようになっている.それをやっているのはオブジェクトのデストラクタではないかと思うのだが…この枝リストが空になるタイミングを掴まえるしかないのではないか?⇒こういうときは迷わずSWOを使うしかない.

不良は#1450のEDGELILSTで起きている.⇒カーソルが動かなくなってしまった.開発機だけでなくサブマシンもハング状態になっている.サブマシンを再起動して復旧した.SWOの設定を誤っていたのが原因と思われる.SWOはCPUパワーをいきなり食うのでヒットしないで爆走するようになるとすべてが止まってしまう可能性がある.

SIMPLEGRAPH::add→NLIST::putでnew SIMPLEEDGEを実行→SIMPLEEDGEのコンストラクタで停止した.ここでtriggerを掛けてもう一度走らせてみよう.LIST::_removeを実行しているところだ.

▲NODULE::_SLOTの論理はかなりおかしい.これではまったく動作しないように思われる.(edan >= 0)のとき,

if (edan <= SLOTsNODULE) return (nodule*&)bnod[edan];
else throw ERR_WRONGSLOTINDEX;

となっているが,SLOTsNODULEは定数で値はゼロだ.この関数はNODULEのメンバーで仮想関数ではないから,決めつけの動作になっている.これを呼び出しているのはNODULE::checkpnodeという検査関数でデバッグ時しか実行されていないが…対象ノードはnodl_float中だが,すべてのスロットにアクセスできなければおかしい.checkpnodeの参照は14箇所.いや,_SLOTという関数はCOMMONHEADERSHORTで定義されているので,すべてのクラスが固有関数を持っている.従って,NODULE::_SLOTがこのような動作になっているというのは必ずしも誤りとは言えない.しかし,明らかにcheckpnodeが意図している動作にはなっていない._DEBUG_で止めてある関数いは少し仕様が古いものがあるのではないだろうか?デバッグルーチンのデバッグになってしまうので,あとで見ることにしよう.

対象となっているリスト要素のSIMPLEEDGEがnodl_floatに入ったときには,すでにpnodは空になっている.pnumもゼロだ.nodule:operator deleteではオブジェクトはすべてゴミ箱に一旦廃棄される.これを実行しているのはTRASHCAN::DumpWasteだ.ここに入った時点ですでにpnodは空だ.nodule::Disposeでnodl_floatを実行している.ここではtoplistは一旦空になっている.明らかにここで誤っている.増設スロットEXTRA_ANCHORの動作をコピーしてすっかり同じことをやっている.これは完全な誤りだ.

nodl_floatは接続を切断するための操作であり,参照操作を行うことは予定されていない.ノードを削除する目的のときは事前に参照はすべて削除されているはずだが,そうでない場合もある.たとえば活きたまま別の位置に繋ぎ替えるときなどにもnodl_floatが実施される.従って,ここでは参照リストに関しては何もしないというのが正しい.⇒動くようになったが,DATALIST::cancelでデータカウント残が出ている.

障害が起きているのは要素を23個持ったCOMPLISTで先頭ノードを1個削除しただけでtoplistが空になっている.cancelはもともとDATALISTの関数だが,DATALISTはbottomlistを持っていないのでLISTでcancelしなくてはならない.ただし,これはいまの障害の動作とは関係ない.ゼロスロットの繋ぎ変えはelseケースで実行していたが,REFERISTのコードが残っていたため実行されなかった.

かなり難しい問題が出てきた.フロート状態のオブジェクトの参照管理は不可能かもしれない.ともかく一度バックアップを取っておこう.

◎検索ボックスをタイトルバーに出す.もし,それができればつねに検索ボックスが見えるようにすることも可能になるのだが…

nodule::ReferenceControlで(!referist || !*referist)により停止する.*referistが空になっている.これはこのノードがフロート状態になっていることを意味する.referistは参照元ノードのアドレスが格納されているアドレスだが,参照リストに保存するのは単なるアドレスで問題ないのではないか?一度生成されたオブジェクトは生成された場所から移動することはあり得ないと考えられる.ただし,この場合に問題になるのは無断で削除された場合にどうなるか?という点だ.これについては少し吟味しなくてはならない.

このシステムは自前でリサイクルシステムを持っていてdeleteでパージされたオブジェクトも実体的には(ゴミ箱の中で)存続しているので,GP例外のような致命的なエラーは発生しないと考えられる.どこにも接続していないフロート状態というのはかなり不安定な状態ではあるが,完全参照リストシステム自体は接続とは無関係に,つまりフロート状態のノードを含めて維持できるはずであるし,そうしなくてはならないと思う.⇒修正を入れてみよう.

DATALIST::findlinkという関数がダブルポインタを使っている.ダブルポインタはスロットに格納されていないと取得できないが,代用として&を付けるという便法がある.これで通るはずだ.⇒#8のTITLEBOX→#6のTREEVIEWという参照を解除しようとして,TREEVIEWの参照リストに記載がないというエラーになった.おかしい.目の錯覚だろうか?.⇒TITLEBOX→TREEVIEWではなく,TREEVIEW→TITLEBOXになっている.#8 TITLEBOX は一度も参照設定していないのに,参照リンクに値が入っている.これはかなりおかしい.BOBsYUPPER(3)という枝だ.⇒いや,やっている.InitTreeView→initializeでSetBranchからSansyoが実行されている.

これは誤動作ではないかと思う.ノードAからノードBへの参照が多重に掛かる場合があり得る.それに対処していないのでは?参照削除されたときは単純にリストから削除しているが,これは明らかに誤りだ.A(i)→BとA(j)→Bは異なるものとして扱うか,ないし参照カウントを管理しなくてはならない.もっとも確実なのは(参照元,枝番号)の対で1エントリとする方式ではないだろうか?このためには(参照元,枝番号)というタイプのクラスないし,構造体の定義が必要だ.クラスにするのも大げさなので構造体を作ってみよう.構造体などしばらく使っていないので構文を忘れてしまった.こんな↓感じでいいかな?

typedef struct _NODEREF {
     nodule *node;
     int edan;
} NODEREF;

しかし,DATALISTと言いながら,データの一致で検索する関数を持っていないとは…nodule *DATALIST::finddata(void *dp)という関数を作った.⇒これで問題は解決したように思われが,またtoplistがあってbottomlistがないという問題が再燃してしまった.さっきは,修正コードに手抜きがあったためだが,こんどは何だろう?障害はTRIBEBOX::CheckGeneSplitで起きている.TRIBEBOX::SpllitListというLISTのインスタンスだ.

見落としていたが,ARRAYクラスの派生クラスとしてREFLINKというクラスがある.これはスロットを1個だけ持つnoduleだ.リスト要素として用いることが想定されている.DATALISTではデータサイズを指定しないときはデフォルトでこのタイプの要素を持つリストを生成する.

障害はDATALIST::putを実行したところで起きている.putはもともとDATALISTの関数だが,LISTの関数でオーバーライドしないとまずい.実際,DATALISTのputではBottomlistが欠けている.不足しているLISTの関数をすべて補充して動作するようになった.それにしても,A→Bへの参照が多重に掛かるという問題などはプロトタイプのReferenceListのときから存在していたはずだが,一度も露見したことがないというのは怖い話だ.ReferenceListはかなり強力なツールなので結構使っているにも関わらず一度も発現したことはない.A→Bへの参照が複数存在するノードというのはかなり特殊で,たまたまそれらに関係するトラブルが発生しなかったということだが,「動いているから安心」など決して言えないことが分かった.⇒ダメだ!まだ何かある.

▲構成図 TEST.zelをUNDOCHAINで開いて,graph3でソートしただけでエラーになった.被参照カウントの残留だ.障害はnoduleのデストラクタで検出されている.TOPOLOGY::topologicalSortの冒頭でbaselistをキャンセルしたところだ.障害が起きているのは#43490のPAIRBOXだ.このノードの完全参照リストを見ると確かに要素が1個残っている.ダンプしてみよう.

DumpRefList PAIRBOX::Dispose #43490 ▲【p PAIRBOX 43490】
   1 edan=2 node=PAIRLIST:4647 Pindex=0 datacount=0 gene=-1
   2 edan=2 node=#28360 ノード対:MDB(2)→(0) ch=2
   3 edan=7 node=COUPLING(0) Bobject::pid=B cid=n DEL=0 #765 nam0=NAMEBOX nam1=765
   4 edan=18 node=COUPLING(1) Bobject::pid=B cid=n DEL=0 #43373 nam0=NAMEBOX nam1=43373
   5 edan=3 node=PAIRLIST:4647 Pindex=0 datacount=0 gene=-1

枝2と3にPAIRLIST #4647 からの参照がある.残っているのは枝2の方だ.PAIRLISTの枝2はbottom2,枝3はcurnodなのでbottom2が残っているように見える.PAIRLISTは独自のcancel関数を持っている.

コメントを残す

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

CAPTCHA