TIP 383: Injecting Code into Suspended Coroutines

Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2017 Conference, Houston/TX, US, Oct 16-20
Send your abstracts to tclconference@googlegroups.com
by Aug 21.
Author:         Alexandre Ferrieux <alexandre.ferrieux@gmail.com>
Author:         Miguel Sofer <msofer@users.sourceforge.net>
State:          Draft
Type:           Project
Vote:           Pending
Created:        03-Dec-2010
Keywords:       debugging,coroutine,yielded
Tcl-Version:    8.7


This proposes a new command, coroinject, that allows a programmer to inject arbitrary code into a suspended coroutine, for execution on next resumption.


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.


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


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.


This document has been placed in the public domain.