Gauche > Archives > 2020/06/14

2020/06/14 00:34:25 UTCnkoguro
#
限定継続の件ですが、別のケースでVMのassertionに引っかかるようです。
#
https://gist.github.com/nkoguro/bcb23f9a1913cfced2817680d3fcfb46
#
% gosh pcdemo8.scm
==> call next
start
end
==> call next
start
start
end
end
catch error!!
"./vminsn.scm", line 1442: Assertion failed: SCM_PAIRP((vm)->handlers)
VM 0x7ff77f54ac00 -----------------------------------------------------------
   pc: 0x7ff77bb83e50 [   16(0x7ff77bb83dd0)] (0000000e)
   sp: 0x7ff77f54d020  [0x7ff77f54d000-0x7ff77f54d000-0x7ff77f560880]
 argp: 0x7ff77f54d020
 val0: #<undef>
 envs:
   0x7ff77f54d008 #f
       up=0x7ff77bb86430 size=1
       [ (#<undef>) ]
   0x7ff77bb86430 #f
       up=0x7ff77bb8c5e8 size=2
       [ #<closure ((main #:G5))> #<closure ((main #:G6))> ]
   0x7ff77bb8c5e8 #f
       up=0x7ff77bb8c5c8 size=1
       [ #<closure ((main #:G7))> ]
   0x7ff77bb8c5c8 (main args)
       up=0x7ff77bbc54a8 size=1
       [ ("pcdemo8.scm") ]
   0x7ff77bbc54a8 #f
       up=0x7ff77bb8c688 size=5
       [ #<closure ((main #:G5))> #<closure ((main #:G6))> #<closure ((main #:G7))> #<closure ((main #:G8))> #<closure ((main #:G9) e)> ]
   0x7ff77bb8c688 #f
       up=0x7ff77bb24700 size=1
       [ (define (main args) (enqueue! queue (lambda () (guard (e (else (print "catch error!!"))) (yield) (error "err")))) (while (dequeue! queue #f) => next (print "==> call next") (reset (dynamic-wind (lambda () (print "start")) next (lambda () (print "end"))))) 0) ]
   0x7ff77bb24700 #f
       up=0x7ff77bc38580 size=2
       [ #<closure ((load-from-port read+) port)> #<closure ((load-from-port restore-load-context))> ]
   0x7ff77bc38580 #f
       up=0x7ff77bb41fe8 size=8
       [ #<read-context 0x7ff77bb3b000> #<read-context 0x7ff77bd1e5a0> 0 permissive () () #f #<module user> ]
   0x7ff77bb41fe8 #f
       up=0x7ff77bb41fc8 size=1
       [ #f ]
   0x7ff77bb41fc8 #f
       up=0x7ff77bb246d8 size=1
       [ () ]
   0x7ff77bb246d8 #f
       up=0x7ff77bb246a0 size=3
       [ #f () () ]
   0x7ff77bb246a0 (load-from-port port :key (paths #f) (environment #f))
       up=(nil) size=2
       [ (:environment #f :paths ()) #<iport(closed) ./pcdemo8.scm 0x7ff77bb313c0> ]
conts:
C stacks:
  0x7ffc069f4830: prev=0x7ffc06
#
assertionに引っかかる他に、2つ疑問な個所があります。
#
(1) 継続を呼び出した後(2回目の"==> call next")、dynamic-windのbefore/afterが2回呼び出されている("start""start" "end""end"と2回表示されている)。これは、本来は1回だけだと思います。
#
(2) guardがdynamic-windのあとに呼び出されている。dynamic-windの中にguardがあるから、"start" "catch error!!" "end"の順に表示されるかと思います
2020/06/14 04:34:38 UTCshiro
#
>kaki (naive-factorize 1) => () にしました。
2020/06/14 06:15:24 UTCkaki
#
ありがとうございます。
2020/06/14 07:30:51 UTChamayama
#
pcdemo8.scm についてですが、まず reset dw1 gu shift ... となっているので、
dw1 と gu のハンドラが切り取られます。
そして、2周目は reset dw2 cont となっていて、cont を起動するときに、
その時点のハンドラ dw2 に切り取ったハンドラが追加されるため、dw2 dw gu となって起動されます。
このため、正常に終了した場合に、start(dw2) start(dw1) end(dw1) end(dw2) となるのは、
想定通りということになります。
#
問題は、error を発生させた場合ですが、多分ここでフル継続のジャンプになっていて、
スタックやハンドラが巻き戻って、何か起きているのではないかと、
時間がとれたらまた見てみます。。。
2020/06/14 07:46:41 UTCnkoguro
#
見ていただきありがとうございます。(1)のdynamic-windの件は了解です。
#
ただ、dynamic-windto
#
dynamic-windとresetの位置を入れ替えた https://gist.github.com/nkoguro/a212a14bc6478b68dee5f0790e97ac1f を実行しても同様にstart, endが2回表示されます。
#
% gosh pcdemo9.scm
==> call next
start
end
==> call next
start
start
end
end
catch error!!
"./vminsn.scm", line 1442: Assertion failed: SCM_PAIRP((vm)->handlers)
VM 0x7fd11cb68c00 -----------------------------------------------------------
   pc: 0x7fd1191aef20 [   16(0x7fd1191aeea0)] (0000000e)
   sp: 0x7fd11cb6b020  [0x7fd11cb6b000-0x7fd11cb6b000-0x7fd11cb7e880]
 argp: 0x7fd11cb6b020
 val0: #<undef>
(以下同様)
#
pcdemo9.scmではresetでdynamic-windが切り取られているから、shiftで捕捉された継続にはdynamic-windのbefore/afterは含まれないと思うのですがどうでしょう?
2020/06/14 08:00:21 UTCnkoguro
#
あっ、すみません。さっきのpcdemo9.scmは使っていないコード部分のresetを変えただけでした。
#
修正版のpcdemo10.scmだと、start/endの個数は1個になりますが、"attempt to return from a ghost continuation.
"のエラーが発生するようです。
#
% gosh pcdemo10.scm
==> call next
start
end
==> call next
start
end
catch error!!
*** ERROR: attempt to return from a ghost continuation.
Stack Trace:
_______________________________________
  0  (%reset (^ () (next)))
        expanded from (reset (next))
        at "./pcdemo10.scm":23
  1  (do ((next (dequeue! queue #f) (dequeue! queue #f))) ((not ne ...
        expanded from (while (dequeue! queue #f) => next (print "==> call next") (
        at "./pcdemo10.scm":16
"vm.c", line 1982 (dynwind_body_cc): Assertion failed: SCM_PAIRP(vm->handlers)
2020/06/14 08:55:19 UTChamayama
#
うむ、多分ですが、error によるジャンプはフル継続のジャンプになっていて、
初回の reset dw1 gu shift の gu のところにジャンプしているかと。。。
つまり、shift で切り取るより前の時点に戻っています。
しかし、そのまま続行すると、部分継続の終端に到達します。
(shift の実行時に終端マーカーをセットしているので、戻ってもマーカーはある)
#
ここで、reset を突き抜けて実行を続けられればよいのかもしれませんが、
実装の制約とかいろいろあって、多分どこかに戻ろうとします。
このときに何か不整合が発生しているかと。
#
多分、以下のページの既知の問題点の亜種と思う。
https://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3A%E9%83%A8%E5%88%86%E7%B6%99%E7%B6%9A%E3%81%A8%E5%8B%95%E7%9A%84%E7%92%B0%E5%A2%83%E3%81%AE%E5%AE%9F%E9%A8%93
2020/06/14 09:04:54 UTChamayama
#
当時は、「フル継続のジャンプ後に部分継続の終端に到達した場合の動作は未定義です」
でもうよいのでは、と思ってうやむやになっていたのですが、
実例が出てくるとは。。。
2020/06/14 10:23:33 UTCnkoguro
#
なるほど。ちなみに先のpcdemo10.scmのコードは以下のものです(リンク張り忘れてました)
#
https://gist.github.com/nkoguro/13ba5257847507e637416aa92a2e889c
2020/06/14 17:20:41 UTCkaki
#
ちょっと議論についていけてないのですが、 https://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3A%E9%83%A8%E5%88%86%E7%B6%99%E7%B6%9A%E3%81%A8%E5%8B%95%E7%9A%84%E7%92%B0%E5%A2%83%E3%81%AE%E5%AE%9F%E9%A8%93 のRacketでリークする例は正しくリークしていると思います。Racketの call/cc で捕捉した継続は、「起動しようとする継続と現在の継続で継続フレームを共有している場合は,そのような最初の継続フレームまでが破棄される.さもなくば,直近の継続プロンプトまでの継続が破棄される.」みたいなことが書いてあるので、この例だと最初の reset までの継続しか破棄されません。 reset がどんどん積み重なっていくことになると思います。
2020/06/14 20:50:36 UTChamayama
#
うむ、そこは迷っているところで、要は既存のアプリがリークなしで動いていたものを、リークするように変更しても、許されるのかという。。。
#
例えば、Guile も call/cc は昔のままで、reset/shift だけを Racket を参考に実装しているようでした。
#
ただ、意味論の異なる旧 call/cc と、reset/shift を混ぜたために、今回の問題が発生しているような気はします。