Ticket UUID: | f9800d52bd61f240938dbd3c3c6364c8a6184194 | |||
Title: | vwait is not NRE-enabled, and yieldto cannot find the right splicing spot | |||
Type: | Bug | Version: | ||
Submitter: | pooryorick | Created on: | 2021-06-19 21:36:06 | |
Subsystem: | - New Builtin Commands | Assigned To: | pooryorick | |
Priority: | 5 Medium | Severity: | Critical | |
Status: | Closed | Last Modified: | 2021-06-23 13:01:38 | |
Resolution: | Fixed | Closed By: | jan.nijtmans | |
Closed on: | 2021-06-23 13:01:38 | |||
Description: |
(text/x-fossil-wiki)
At least as of [07f12cf7209a23fa3], core-8-branch, the script below aborts. The trouble begins with the C stack being busy because c2 has been called from with <code>vwait</code>, which is not yet NRE-enabled. Although the error message talks about tailcall, it is really about <code>yieldto</code>, which calls <code>TclSetTailcall</code>. <code><verbatim> proc main_coro {} { set done [catch { coroutine c1 p0 yieldto c1 coroutine c2 p1 after 0 [list [namespace which c2]] vwait forever } cres copts] } proc p0 {} { yield ::tailcall main } proc p1 {} { yield set this [info coroutine] set script [list ::yieldto [list $this]] yieldto try $script on error {cres copt} " puts \[list uh-oh \$cres] [list $this] " set script [list ::yieldto $this] yieldto ::try $script } coroutine main main_coro </verbatim></code> <b>Output:</b> <verbatim> uh-oh {cannot yield: C stack busy} tailcall cannot find the right splicing spot: should not happen! Aborted (core dumped) </verbatim> | |||
User Comments: |
jan.nijtmans added on 2021-06-23 13:01:38:
(text/x-fossil-wiki)
Merged (again) to core-8-branch (and trunk). Appears to work now. Thanks for the fix, and the updated testcase! jan.nijtmans added on 2021-06-21 14:54:08: (text/x-fossil-wiki) 'Fix' reverted from core-8-branch, because it broke the build. See: [https://github.com/tcltk/tcl/runs/2872891537?check_suite_focus=true] <pre> coroutine.test Test file error: cannot yield: C stack busy while executing "yieldto c1" ("try" body line 1) invoked from within "c2" ("after" script) cannot yield: C stack busy while executing "yieldto c1" ("try" body line 1) ("after" script) </pre> pooryorick added on 2021-06-20 23:10:45: (text/x-fossil-wiki) Fixed in [c75d4c3be05d5e91]. pooryorick added on 2021-06-20 23:10:13: (text/x-fossil-wiki) The problem is that <code>TclSetTailcall<code> splices the command to be yielded to into the right place, but when <code>TclNRCoroutineActivateCallback</code> decides the C stack is busy, it doesn't go back and undo the splice. At that point, the splicing spot is already taken and any other attempt to yieldto produces the error, "tailcall cannot find the right splicing spot: should not happen!". From here on out, that command that hasn't been unspliced behaves like a tailcall: When the coroutine ends that command takes its place. Tailcall itself doesn't have the same problem as yieldto because tailcall uses a slightly different mechanism to register the tailcall. The solution is to undo that splice of the pending command if yieldto can't proceed. pooryorick added on 2021-06-20 18:28:46: (text/x-fossil-wiki) Here is a slightly smaller script to reproduce the issue: <code><verbatim> proc p0 {} { yield yieldto c1 after 0 c2 vwait forever } proc p1 {} { yield tailcall c0 } proc p2 {} { yield after 0 [list [info coroutine]] yieldto try {yieldto c1} on error {} c1 yieldto try [list yieldto c1] } coroutine c0 p0 coroutine c1 p1 coroutine c2 p2 after 0 [list [namespace which c0]] vwait forever </verbatim></code> dkf added on 2021-06-20 07:45:06: (text/html) Well, this shouldn't crash. It probably should be an error though; I can't comprehend any way to make <code>vwait</code> itself be anything other than NRE-unaware. pooryorick added on 2021-06-20 06:41:00: (text/x-fossil-wiki) sebres noted this issue in [cc592ec70de012df]. |