UNDOのShadowをfreeblock化

極小反例サンプル抽出ツールで全選択が2回入った後,UNDO処理が遅くなるという事象が発生している.バックアップされるオブジェクトの個数が急速に増加しているためだが,その原因はどうも,UndoRedoの動作不良によるものらしい.カード#485を削除すると系図木は3系統に分裂する.このうち始系列を除く2系列は先祖ノードだけの孤立系列で,先祖ノードはそれぞれ,#1294と#5174だ.この時点ではUNDOのコマンドチェーンは,{17, 6, 6, 1}のようになっている.ここで{n1, n2,…}はコマンドのバックアップオブジェクトの個数の並びである.

このあと,カード#497を削除すると,コマンドチェーンは{17, 6, 6, 16, 1}のように変化する.しかし,このあと実行されるUndoでは,チェーンは変化せず,{17, 6, 6, 16, 1}のままになっている.そのまま続行すると,チェーンは{17, 6, 6, 37, 1}のように変化し,チェーンの4番目のコマンドのオブジェクト数は1→ 16→ 37→ 79→ 160→ 366→… のように単調に増加を続けるようになる.そもそも,Undoしているのにコマンドチェーンの長さが短くならないというのがおかしい.⇒いや,勘違いしている.UndoRedoで変化するのはUNDOの現在位置UndoCurptrだけで,コマンドチェーンは不変だ.

従って,カード#497の削除後のUndoで,コマンドチェーンが{17, 6, 6, 16, 1}→ {17, 6, 6, 16, 1}のように変化していないという動作は正しい.実際,UndoCurptrの位置を[]で示すと,{17, 6, 6, 16, [1]}→ {17, 6, 6, [16], 1}のように移動している.この後,カード削除を実行すると,[16]の位置に新たなバックアップが書き込まれるため,その分だけUndoノードが増加する.この辺りの論理は書き換えていないつもりだが,だとすれば以前からこのような動作になっていたことになる…確認してみよう.⇒2022-03-03という版を見てみよう.この版ではUNDO処理はコンパイルオプションで止めてある.⇒… なんということだろう!この版では極小反例 461.zelが何の苦もなく開けてしまった…

image

ということは,これらの反例が開けなくなったのは比較的最近の修正によるということになってしまう.⇒いや,少なくとも「BUG3000-1906.ZEL」は解けない.⇒やはり,解けていないようだ.371点の反例までは解けるが,550点反例では解けなくなる.この事象は計算誤差をどの程度に見込むかなどによって微妙に動作が変化するため,最新版で解けないサンプルが古い版で解けるということもあり得るだろう.

ようやく,なぜこういう事象が起きているのか?という理由がわかった.UNDOでバックアップを取るときのロジックの穴を塞ぐためにUndoCounterというグローバル変数を設け,コマンドを生成するたびにインクリメントするようにして,対象オブジェクトを保全するときにはこの値をオブジェクトに記録し,重複バックアップを回避するように修正しているが,これが却って裏目に出ているということのようだ.UndoCounterはインクリメントするだけの値なので,Undoでコマンドチェーンを遡った状態で新たなコマンドが実行されたときには,前に保全されているオブジェクトであってもUndoCounter値が小さいためにバックアップが再実行されることになる.これを避けるためにはUndoCounterの値をUndoRedoの動作に合わせなくてはならない.

類似パラメータとして,UndoCountというのがある.これはコマンドを生成するたびにインクリメントされるが,ある定数MAXUNDOCOUNTに達したときには,それ以上のコマンド生成を停止し,UndoChainを一つ前に進める.UndoCounterは現状ではMAXUNDOCOUNTに関わりなく,つねに1インクリメントするようになっている.必要な修正はUndoCounterの値をUndoでは1デクリメントし,Redoでは1インクリメントするようにすることだろう.それでよいのかどうか?確認してみよう.⇒ダメだ.さっぱり作動していない.

チェック用のコードなどを入れて,358点まで縮小したサンプルから開始したところ,何点か単点削除検査の後,スタックオーバーフローが発生した.また別の障害が出てきた可能性があるので,元のサンプルでテストし直してみる.⇒コマンドにも時点のUndoCounterを記入するように修正してみたが,全削除2回のあとの単点削除でUndoCounterの不一致が発生した.UndoCurptr->UndoCounterの値は98で,UndoCounterの値は99だ.DumpUndoChainでUndoCounterの値も表示するようにしてみよう.

UndoCounterとUndoCurptrは完全に同期している必要がある.つまり,UndoCurptrが更新される場所ではつねにUndoCounterも更新されなくてはならない.UndoCurptrがリセットされるときには,UndoCounterもゼロリセットされなくてはならないだろう.⇒対処した.UndoCounterはUndoNumberにリネームした.3回目の単点削除→Undo中に発生している.

▲UNDOBASE::UndoProcess→ TOPOLOGY::RebuildCardList→ NAMESORT::NameSort→ NAMESORT::SortNameSub→ compnode のコールバックでcard2の参照番号が0になっている.これまではこのようなことは起きていなかった… 参照番号の入っていないオブジェクトはSWOでもトレースできない.カードテーブルのインデックスが3であるという手掛かりしかない.PDBのsnumは8だ.⇒おかしい.サンプルファイルをロードした時点でこのカードがヒットしない.いや,この[3]というインデックスはカードテーブルではなく,そのルックアップテーブルのインデックスだ.カードテーブルは1発進だが,ルックアップテーブルはゼロ発進になっている.

ルックアップテーブルは毎回作り直ししているので,障害が起きたときのインデックスを参照しても情報がつかめない.⇒これは,参照番号が消えたのではなく,リンクだけが残った状態ではないか?検定では,#23削除→ Undo→ #32削除→ Undo→ #41削除→ Undo で障害が発生している.直近の修正が影響していることは間違いない… ⇒一つ余分なことをしていた.オブジェクトのコピーにUndoNumberを格納していたことが誤動作の原因と思われる.これは不用な動作だった.しかし,依然としてバックアップが増殖してしまう状況は変わらない.

▲nodule::setShadowでエラーが発生した.この関数はShadowチェーンの更新用関数で,オブジェクトのShadowチェーンの末尾に新たに生成されたShadowを繋ぎ込むものだが,対象Shadowイメージにリンクが残っている.このリンクは不正リンクでおそらく,ゴミと思われる.⇒いや,違う.Shadowというのは,オブジェクトの時点におけるコピーだから,その時点でそのオブジェクトがすでにShadowを持っていれば,当然shadow->Shadowには値が入っていると考えられる.

修正した.しかし,いままで一度もここで停止しなかったという点に関しては不審が残る.大分まともな動作になってきたが,まだ検定が進んだ段階でバックアップ個数が再び増加し始める.このまましばらく走らせてどうなるか見てみることにしよう.


コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA