TIP #383: INJECTING CODE INTO SUSPENDED COROUTINES ==================================================== Version: $Revision: 1.4 $ Author: Alexandre Ferrieux Miguel Sofer State: Draft Type: Project Tcl-Version: 8.7 Vote: Pending Created: Friday, 03 December 2010 URL: https://tip.tcl-lang.org383.html Post-History: ------------------------------------------------------------------------- ABSTRACT ========== This proposes a new command, *coroinject*, that allows a programmer to inject arbitrary code into a suspended coroutine, for execution on next resumption. RATIONALE =========== When debugging complex coroutines - with many yield points and possibly rich state in local variables - sometimes one would like to inspect their state "from the outside", i.e., at a point where they are suspended. A typical situation is that of a big, single-threaded, event+coro system, where the coro happily enables/disables fileevents along its life, and the fileevents are one way to resume the coro. At a given point (bug), things get stalled, with the fileevents disabled. The obvious questions are: 1. "where" is the coro (at which yield site)? and 2. what are the values of its local variables? Both these questions can be answered with the new *coroinject* primitive. The idea is to force a resumption of the coro along with an "immediate execution of extra code" directive, where the extra code says "dump the call stack with *info level* and *info frame*", or "dump the locals", etc. Another use would be to inject "*return -code return*", as an alternative to renaming to {} for terminating the coro in a way that respects its *catch*/*finally* termination handlers. Alternatively, returning with an error code will have the effect of gathering call stack information in the *-errorstack* options dictionary entry. At the other end of the spectrum, the injected code can be completely transparent: either with a forced resumption and injected code ending with *yield*, or merely waiting for normal resumption when the app sees fit, and injected code falling back to normal coro code. Note that the feature is similar to a proc-entry trace, but coroutine resumption is not currently a trace target. Also, it is an intrinsically "one-shot" mechanism, which makes it a better fit for its debugging purposes. DEFINITION ============ The new command *coroinject* /coroname/ /cmd/ ?/arg1 .../? prepends to the code to be executed on resumption of the currently suspended coroutine, /coroname/, the following code: /cmd/ /arg1.../ /resumearg/ where /resumearg/ is the single argument passed to the resumption command /coroname/. In turn, the result from the execution of /cmd/ will be seen by the coroutine's code as the result of *yield*. Note that: 1. Resumption itself must be done separately, by calling /coroname/ later, 2. If *coroinject* is called several times on the same /coroname/ before resuming it, the commands pile up in LIFO order. 3. In combination, the appending of /resumearg/ and the use of the result of /cmd/ to provide the result of *yield*, will allow the following style of fully transparent injection: proc probe {x y resumearg} {do things $x $y;return $resumearg} coroinject C probe foo bar NAMING ======== Of course, the proposed *coroinject* is a placeholder for a suitable name. Alternatives that also make sense are: *::tcl::coroinject* and *interp coroinject*. Constructive bikeshedding welcome. REFERENCE IMPLEMENTATION ========================== The current *::tcl::unsupported::inject* implements most of the functionality described here, minus the /resumearg/ passing. It will be updated to include it if consensus gathers on this style. COPYRIGHT =========== This document has been placed in the public domain. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows