2009年3月13日金曜日

weakrefの話

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Modules/weakref.txtを読んでいる。

一般にdeleteしたobjectが持っていた領域にあるpointer呼んではいけない。new/deleteって契約だから。newはこの部分のメモリをobjectとして振舞わせますという通告だし、deleteはobjectではなくなりましたっていう通告だ。問題としては、かなり古典だ。Destructors @ C++ FAQ Liteとか。

1度deleteしてしまったら、一般的には復活させることはできないだろう。該当object持っているメンバを再帰的に復活させる必要があり、それは自明で無い。memoryは運が悪いとsystemのmalloc/freeのfreeが呼ばれてしまい、OSが回収してしまうかもしれない。memory以外のresource(たとえばnetworkとかlockとか)をもっていればなおさらで、同じ質で再獲得できる保障はどこにもない。

これはgcの問題(いつdestructorを呼ぶか?)というよりdestructorへのhookをdestructorを実行した後に呼んではイケマセンという話だと思うのだが。

一方でweakrefは指しているobjectがinvalidになったことを通知してもらわないと、困る。なぜかというとrefの仕様を読めばわかるが、wr = ref(referent)という形で作られ、
wr is not Noneで判定する。この辺。wrはproxyになっていて、

  • referentが存在するならreferent

  • referentが存在しないならNone


を返す。Noneを返すためには、refの実装が状態を持ってreferentが死んだことをgcから通知してもらい、死亡フラグを立てる必要がある。そうしないと死後長くたってから問い合わせがくると、応えることができない。weakrefを使う状況は、この辺とかがわかりやすい。使い手に通知するようなコードがかけない、書きにくいときに、通知をgc機構に代行してもらうといった感じだ。

ではその通知をgc機構がいつするかといことだ。

結論はこうなったらしい。

Jim Fulton gave the best nutshell summary of the new (in 2.4 and 2.3.5)
approach:

Clearing cyclic trash can call Python code. If there are weakrefs to
any of the cyclic trash, then those weakrefs can be used to resurrect
the objects. Therefore, *before* clearing cyclic trash, we need to
remove any weakrefs. If any of the weakrefs being removed have
callbacks, then we need to save the callbacks and call them *after* all
of the weakrefs have been cleared.


deleteしてしまったらobjectでありえないので、当然weakrefを先に解決しなければならない。なので、消そうと思っているobjectにくっついているweakrefに対して通知し、消そうと思っているobjectに対してのweakrefを解決してから、消すということになった。おのおのの__del__を呼ぶ直前ではなく、cyclicな「塊」に属するobjectすべてのweakrefを先に取り除いてからとなる。__del__が処理される瞬間に関して理解が怪しいのでアレだが、多分PyObjectのdestructor相当の関数の中でcallされるのだろう。__del__なしでcyclicなclassというくだりがあるところを見るとね。


An alternative would have been to treat objects with callbacks like objects
with __del__ methods, refusing to collect them, appending them to gc.garbage
instead. That would have been much easier. Jim Fulton gave a strong
argument against that (on Python-Dev):

There's a big difference between __del__ and weakref callbacks.
The __del__ method is "internal" to a design. When you design a
class with a del method, you know you have to avoid including the
class in cycles.

Now, suppose you have a design that makes has no __del__ methods but
that does use cyclic data structures. You reason about the design,
run tests, and convince yourself you don't have a leak.

Now, suppose some external code creates a weakref to one of your
objects. All of a sudden, you start leaking. You can look at your
code all you want and you won't find a reason for the leak.


メモ:

常にNoneを返すwrの実装は実用にはならないが、プログラムをクラッシュさせたりLeakしたりすることは無い。だからgcがまだ__del__を呼んだり、メモリ上からobjectを消す前にwrにNoneを返すことを要請することは間違ってはいない。一方で、その逆はreferentが死んでいると一番最初に書いた契約を破ることになり、プログラムがクラッシュする。

0 件のコメント: