アプリから完全独立な抽象UNDO機能系

UNDOシステムの理想はアプリケーションから完全に独立な抽象機能系だ.UNDOはアプリが何をやっているのかを知らなくても対象オブジェクトの形式的な解析だけで保全する範囲・手順を決定できるものとする.そのようなものが存在するとすれば,アプリケーションではUNDOをまったく意識せずに自由にアプリ固有のコードを書くことができるし,システムメンテナンスのコストも大幅に削減されるだろう.そんなことが可能だろうか?もちろん,原理的には可能だ.コマンド実行ごとに系全体を「バージョン」としてバックアップするだけで簡単・確実に実現できる.ただし,この方式はコスト的に見て現実的ではない.

たとえば,テキストエディタのようなものであれば,テキスト編集は①挿入と②削除の2つのプリミティブな操作に還元できると考えられるから,①の場合は,位置と挿入文字列,②の場合は位置と削除文字数を記録するだけで基本的なUNDO機能をサポートできるかもしれない.これは言ってみればミニマムなUNDO系の実現と言える.しかし,ZTの場合なら,たとえば「カード登録」という一つのコマンドで,複数の関係(親子,結婚,兄弟など)の変化が同時多発的に発生し,それらが相互にどう影響し合うのかもにわかには判別できない.

しかし,完全な参照管理が実現された超クリーンで透明なシステムであればひょっとしてそのようなことができるのではないかと期待することはできる.ZTのUNDO機能はエラーが発生したときにUNDOで戻ってそれを再現できる程度には強力だが,まだそこまでの抽象度には達していない.ともあれ,まずいま出ている目先の問題から片付けることにしよう.どうもこの不良はシステム的なもの(設計不良)というより,むしろバグ(論理不良)ではないかという気がしてきた.少なくとも見た限りではNring上に同一snum(システム通番)を持つ複数のオブジェクトが存在しているように見える.まず,この点を確認してみよう.

誤読していた.Nring上には重複は存在しない.重複していたのはUNDONODEからの参照先だ.ただし,UNDONODEは複製ノードと一対一に対応していたはずだから,やはり重複があるというのは誤りなのではないか?⇒複製ノードが同一snumを持つ場合は当然あり得る.もちろん,実体が同じなら重複していることになるが… 問題なさそうだ.

「Nringとゴミ箱にダブル登録」という問題を考えなくてはならないが,その前にカード削除で新たにCARDLINKとMARGLINKが生成されるという動作を調べてみよう.⇒どうもまだ勘違いしているようだ.CARDLINKとMARGLINKがNringに残る問題と「Nringとゴミ箱にダブル登録」問題は完全に一つの問題だ.わかり易くするためにはダブル登録を止めるしかない.ゴミ箱に入っているオブジェクトはリサイクル可能というのが本来の趣旨なのだから,Shadow付きオブジェクトはリサイクル不可としてNringに置くべきなのではないか?⇒そのように決定.

nodule::DELETEDの値を{-1, 0, 1}ではなく,明示的に{PROLONG, EXISTING, DEAD}で示すようにしておこう.⇒nodule::operator deleteでShadow付きオブジェクトはゴミ箱に移動しないように修正して,CheckWasteCountでカウント不一致になった.⇒これまではNringのカウントとしてアクティブノードだけを計上していたが,延命ノードの重複が解消したので,全ノードをカウントする必要がある.⇒これで中間の集計はすべて一致するようになったが,アプリ終了の出口関数で不一致になった.ゴミ箱の中身が25点少なくなっている.

この差分はReleaseRefListの実行によって起きている.いや,違う.EraseFamilyTreeの中だ.UNDOSYSTEM::resetUndoChainだ.⇒UNDOSYSTEM::CheckUndoChainでエラーが出た.親ノード不在(!undo->address->getpnode())というエラーだが,これは「Nringとゴミ箱の重複登録を禁止@20201213」の結果で問題ない.ゴミ箱に入っているということは親を持っているということになるが,Nringに入っているPROLONGノードは親を持たないフロート状態になっている.

▲初回のEraseFamilyTreeのとき,すでにMemoryBlockCount=32 MemoryBlockSize= 143564がリザーブされている.常駐させるか,ないし,取得を遅延させるか.

▲UNDONODE::DisposeでShadowオブジェクトを直接delmem_でメモリからパージしている.実際にはこの前段でfreeblockはすべて解放されているので,実際の効果はないのだが,あまりよいマナーではない.ここはfreeblockのアドレスを取得して delete するべきだ.

UNDONODE::DisposeでPROLONGオブジェクトの始末を付けるようにした.⇒エラーはすべて解消した.手順としては,フリーブロックの解放はUNDOSYSTEMのリセットより後にするべきだと思う.順序を入れ替えてみよう.⇒UNDONODE::Disposeでshadowを保持するfreeblockをdeleteしようとしてエラーになった._getblock_でfreeblockの逆引きができない.shadowはfreeblock::getmemで取得したアドレスなので,_getblock_でfreeblockのアドレスを取れるはずでは…

いや,違う.勘違いしていた.shadowは完全なフリーメモリだ.つまり,getmemで取得し,delmem_でリリースするという類のものだ.delmem_は下位関数としてdelmemを呼び出しているが,バランスが悪い.getmemの対向としてはdelmemの方がよい.名前を取り替えておこう.⇒現行ではアプリ終了時にゴミ箱内の廃棄物を処分している.freeblockもリサイクル可能なのだから,中間で廃棄する必要はないのではないか?さらに言えば,最終的にはfreeblockもゴミ箱に落ちるのだから,アプリ終了時にはゴミ箱の処分だけやればよいのではないか?

上記の通り,freeblock::ReleaseFreeBlockを止めてTRASHCANに任せたところ,freeblockが2つ残留した.#34と#36だ.基本的にfreeblockは作成者が責任をもって始末すべきものだ.⇒#34はBUNSFILE::openで取得しているファイル読み込みのためのバッファだ.この関数が呼ばれるたびに,前回取得したメモリを解放しているので,関数の出口で解放してよいのではないか?closeという関数はある.BUNSFILE::closeには,「オブジェクト存続中はバッファbunsbuffを維持する」という説明が付いている.~BUNSFILEで解放している.

QUICKDBはKAKEIZUの埋め込みオブジェクトなのでデストラクタは呼び出されない.KAKEIZUはQUICKDBを2つ持っているが,これらをnewで生成してやれば,明示的にdeleteすることもできるのだが… ファイルをクローズしてもバッファを使うというのは考え難いので,Closeで廃棄しておこう.closeはCloseFamilyBaseのタイミングで掛かってくるのでまったく問題ない.一度ここでバックアップを取っておこう.

freeblockでMemoryBlockCountとMemoryBlockSizeが更新されていない.totalblock, totalsizeは更新されている.⇒いや,totalsizeはゼロのまま変化していない.⇒コードを追加した.totalblockはfreeblockのカウント, totalsizeはfreeblockのヘッダ部を除いたフリーメモリの正味サイズの合計だ.Couplingを削除してMemoryBlockCount=33 MemoryBlockSize=143756まで落ちる.メモリリークの可能性はあるが,CrtDumpMemoryLeaksのダンプはもっとずっと小さな数字だ.

★DRAWSTAGE totalblock=42 totalsize=169966 MemoryBlockCount=10033 MemoryBlockSize=6975606 TotalBlockCount=10033 TotalBlockSize=6975606

★INITIALSTATE totalblock=0 totalsize=1291806 MemoryBlockCount=10037 MemoryBlockSize=8098118 TotalBlockCount=10037 TotalBlockSize=8098118

★delete Coupling totalblock=0 totalsize=1291806 MemoryBlockCount=32 MemoryBlockSize=143564 TotalBlockCount=10037 TotalBlockSize=8098118

アプリ終了時でtotalblock 0 totalsize 1291806 MemoryBlockCount 32 MemoryBlockSize 143564 のメモリが紛失していることになる.freeblockではCloseFamilyBase→EraseFamilyTreeの入口で不一致が発生している.KAKEIZU::closeFamilyBaseの中で不一致になる.qfile.closeで起きるようだ.バッファのサイズが変化しているのではないだろうか?⇒freeblock::delmptrでtotalsizeを更新していなかった.freeblockの数字は合うようになったが,MemoryBlockCountは33も残っている.サイズで143756の残がある.

わかり易くするためにfreeblock::delmptrとfreeblock::delmem_を廃止して,_delmem_とdelmemに統合した._delmem_に回ってくる分に関してはMemoryBlockCount,MemoryBlockSizeの更新が入っていなかったので,その論理を追加したところ,MemoryBlockCount=-9 MemoryBlockSize=143588という不整合が出た.MemoryBlockSizeの減量をオブジェクトsizeから取得するようにして,MemoryBlockCount -9 MemoryBlockSize -33770という結果になった.

これはおそらくdeleteで一度ゴミ箱に入ったものが再度deleteされているためと考えられる.freeblock::_delmemやfreeblock::delmemで削除される場合は実際にメモリからパージされているので問題ない.NODULE::operator deleteではfreeblock::_delmem(nptr)でメモリ解放しているので,_delmem_で引くというのはやはり間違っていると思う.つまり,消し忘れが33件残っているということではないだろうか?

コメントを残す

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

CAPTCHA