<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE TIP SYSTEM "http://www.tcl.tk/cgi-bin/tct/tip/tipxml.dtd">
<!-- Converted at Sun Feb 12 11:45:34 GMT 2012 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='285'>
<header><title>Script Cancellation with [interp cancel] and Tcl_CancelEval</title><author address="mailto:joe@mistachkin.com">Joe Mistachkin</author><author address="mailto:dawson@dawsoncowals.com">Dawson Cowals</author><status type='project' state='final' tclversion="8.6" vote='after'>$Revision: 1.11 $</status><history></history><created day='4' month='jun' year='2006' /><keyword>eval cancel unwind terminate runaway async thread safe</keyword></header>
<abstract>This TIP introduces the ability to quickly and safely cancel a script within a specified interpreter from any thread in the process.</abstract>
<body><section title="Key Use-Case Scenario">
<para>When using Tcl inside a context such as a web-browser (e.g. as a plugin), it is often necessary for the execution of a particular script to be terminated cleanly without waiting for the script to get to a point where it is able to respond to events. For example, if the user encounters a page that contains a script that starts a long-running execution but then decides to navigate away from the page, it&apos;s important to stop the script as soon as possible.</para>
<para>But we do not want to stop the script by means of just terminating the thread, as this can result in various resources being still allocated, as browsers are long-running applications. Some of the problems (e.g. memory waste) can be worked around by running the script in a separate process, but there are other resources that aren&apos;t cleaned up that way, such as locked files or shared memory blocks, and it is always better to give the Tcl interpreter itself an opportunity to clean up after itself. Furthermore, there are other possible applications (such as using Tcl to implement COM objects on Windows) where the separate-process method will not work so well.</para>
<para>Instead, what is needed is a way to programmatically make a script stop its execution even when that script is otherwise determined to continue. This is different from a resource limit in that the cancellation is not caused by the exceeding of a predetermined value, but rather by some external event that is possibly even not processed initially by Tcl at all.</para>
</section>
<section title="Technical Rationale">
<para>Currently, once the evaluation of a script has been initiated it will do one of the following:</para>
<itemize><item.i><para>run to completion,</para></item.i><item.i><para>run until it encounters an uncaught error,</para></item.i><item.i><para>run until it exceeds a pre-determined limit as specified in <tipref type="text" tip="143"/>, or</para></item.i><item.i><para>run indefinitely.</para></item.i></itemize>
<para>In each of the cases above, neither the host application nor an interactive user have any recourse to terminate the script prior to it running its course.</para>
<para>There are many situations for which it is absolutely necessary to be able to cancel a running script without its cooperation and without setting an arbitrary limit on the amount of time it can run (<tipref type="text" tip="143"/>). This is especially true in a multi-threaded application embedding environment, or where a user interface is present.</para>
<enumerate><item.e index='1'><para>In the case where the completion time for a script is unknown, non-existent, or non-deterministic a user may want or need to terminate the script prematurely.</para></item.e><item.e index='2'><para>When evaluating an untrusted - or marginally trusted - script in either a safe or standard interpreter, there is a risk that the script might never terminate. In such a situation it is not reasonable to forcibly terminate the thread or the entire process.</para><enumerate><item.e index='1'><para>Forcibly terminating a thread prevents Tcl and Tk from cleaning up their thread-specific memory and resources.</para></item.e><item.e index='2'><para>The host application may suffer similar memory and resource leaks as well as other serious side-effects that may corrupt data, prevent other threads from properly synchronizing, or leave the process in an unknown and unrecoverable state.</para></item.e><item.e index='3'><para>For an interactive host application valuable work may be lost without providing an opportunity to save pending modifications. Even in the absence of modifications the host application might have been holding locks that left unreleased would prevent other processes and users from using important resources.</para></item.e></enumerate></item.e></enumerate>
<para>The basic building blocks needed for any scripting language to seamlessly integrate with an enterprise-ready host application are:</para>
<itemize><item.i><para>Engine Initialization</para></item.i><item.i><para>Evaluation</para></item.i><item.i><para>Extensibility</para></item.i><item.i><para>Cancellation</para></item.i><item.i><para>Engine Finalization</para></item.i></itemize>
<para>Tcl now provides full support for all of the above except script cancellation. <tipref type="text" tip="143"/> allows for scripts to be prematurely terminated after reaching resource limits that were pre-arranged by the host application. However, this only handles terminating scripts based on a narrow set of deterministic criteria. Full support would require the ability to immediately and unconditionally terminate the evaluation of a script without adversely affecting the execution environment of the host application. In addition the following issues must be addressed:</para>
<itemize><item.i><para>Scripts being evaluated in nested slave interpreters.</para></item.i><item.i><para>Interaction with third-party extensions.</para></item.i><item.i><para>Safely usable by arbitrary threads.</para></item.i></itemize>
<para>Several other mainstream scripting engines (e.g., JavaScript, Microsoft Active Scripting, etc.) currently provide this capability to cancel the evaluation of a script. This TIP proposes an implementation that would bring this necessary aspect of application integration to Tcl. This must be implemented in the core, because access to and modification of internal Tcl functions and data structures is required.</para>
</section>
<section title="Specification">
<para>A new <emph style="bold">interp cancel</emph> script command will be added, as follows:</para>
<quote><emph style="bold">interp cancel</emph> ?<emph style="bold">-unwind</emph>? ?<emph style="bold">--</emph>? ?<emph style="italic">path</emph>? ?<emph style="italic">result</emph>?</quote>
<para>This command cancels the script being evaluated in the interpreter.</para>
<subsection title="Arguments">
<quote><emph style="bold">-unwind</emph></quote>
<para>This argument is optional. Without <emph style="italic">-unwind</emph>, the evaluation stack for the interpreter is unwound until an enclosing <emph style="bold">catch</emph> command is found or there are no further invocations of the interpreter left on the call-stack. With <emph style="italic">-unwind</emph>, the evaluation stack for the interpreter is unwound without regard to any intervening <emph style="bold">catch</emph> command until there are no further invocations of the interpreter left on the call-stack.</para>
<quote><emph style="bold">--</emph></quote>
<para>This argument is optional, and marks the end of options. The argument following this one will be treated as being the <emph style="italic">path</emph> argument even if it starts with a &quot;-&quot;.</para>
<quote><emph style="italic">path</emph></quote>
<para>This argument is optional. If not supplied, the current interpreter is assumed; otherwise, the interpreter specified by <emph style="italic">path</emph> is used.</para>
<quote><emph style="italic">result</emph></quote>
<para>This argument is optional. If not supplied, a default error message is left in the result of the interpreter; otherwise, the result specified by <emph style="italic">result</emph> is used.</para>
</subsection>
<subsection title="Behavior">
<para>When a script is canceled, the following occur:</para>
<itemize><item.i><para>The <emph style="italic">CANCELED</emph> flag, and possibly the <emph style="italic">TCL_CANCEL_UNWIND</emph> flag, are set in the interpreter to mark the evaluation in progress as having been canceled.</para></item.i><item.i><para>The currently executing command/script in the interpreter is made to return with code <emph style="italic">TCL_ERROR</emph>. (This is superior to using a novel return code, as third-party extensions are usually far better at handling error cases!)</para></item.i><item.i><para>The <emph style="bold">catch</emph> command will only catch errors if the interpreter containing it does not have the <emph style="italic">TCL_CANCEL_UNWIND</emph> flag set.</para></item.i><item.i><para>Additional trips through the internal loops of the <emph style="bold">after</emph>, <emph style="bold">vwait</emph>, <emph style="bold">update</emph> and <emph style="bold">tkwait</emph> commands will not proceed with the <emph style="italic">CANCELED</emph> or <emph style="italic">TCL_CANCEL_UNWIND</emph> flags set. (Extensions can find this information out by using <emph style="italic">Tcl_Canceled</emph>; see below.)</para></item.i><item.i><para>Once the execution unwinds out of the interpreter, so that no further invocations of the interpreter are left on the call-stack, both of the script cancellation related flags are reset.</para></item.i><item.i><para>If there are no invocations of the interpreter on the call-stack when <emph style="italic">Tcl_CancelEval</emph> or <emph style="bold">interp cancel</emph> are called, then the next script to be evaluated will be preemptively canceled.</para></item.i></itemize>
</subsection>
<subsection title="Notes">
<itemize><item.i><para>Going forward, all &quot;long running commands&quot; in the Tcl/Tk core should make every effort to comply with the script cancellation functionality by calling <emph style="italic">Tcl_Canceled</emph> at times when it is appropriate to abort processing.</para></item.i><item.i><para>Extensions can optionally check if they should abort processing by calling <emph style="italic">Tcl_Canceled</emph>.</para></item.i></itemize>
</subsection>
</section>
<section title="C API">

<subsection title="Constants">
<describe><item.d name='TCL_CANCEL_UNWIND'><para>New eval-flag bit that applies to <emph style="italic">Tcl_CancelEval</emph> and <emph style="italic">Tcl_Canceled</emph>.</para><para>When used in <emph style="italic">flags</emph> for <emph style="italic">Tcl_CancelEval</emph>, the evaluation stack for <emph style="italic">interp</emph> is unwound without regard to any intervening <emph style="bold">catch</emph> command until there are no further invocations of <emph style="italic">interp</emph> left on the call stack. When not set, the evaluation stack for the interpreter is unwound until an enclosing <emph style="bold">catch</emph> command is found or there are no further invocations of <emph style="italic">interp</emph> left on the call-stack.</para><para>When used in <emph style="italic">flags</emph> for <emph style="italic">Tcl_Canceled</emph>, checks if the script being evaluated has been canceled using the <emph style="italic">TCL_CANCEL_UNWIND</emph> flag (i.e., the evaluation stack for <emph style="italic">interp</emph> is being completely unwound).</para></item.d><item.d name='TCL_LEAVE_ERR_MSG'><para>Existing variable-related flag bit that applies to <emph style="italic">Tcl_Canceled</emph> only.</para><para>When used in <emph style="italic">flags</emph> for <emph style="italic">Tcl_Canceled</emph>, an error message will be left in the result of <emph style="italic">interp</emph> if the script being evaluated has been canceled.</para></item.d></describe>
</subsection>
<subsection title="Functions">
<quote>int <emph style="bold">Tcl_CancelEval</emph>(Tcl_Interp *<emph style="italic">interp</emph>, Tcl_Obj *<emph style="italic">resultObjPtr</emph>, ClientData <emph style="italic">clientData</emph>, int <emph style="italic">flags</emph>)</quote>
<para>The <emph style="bold">Tcl_CancelEval</emph> function initiates cancellation of the script being evaluated in <emph style="italic">interp</emph>. It returns a standard Tcl result. If <emph style="italic">resultObjPtr</emph> is NULL, a default error message will be left in the result of <emph style="italic">interp</emph> indicating that the script was canceled or unwound. If <emph style="italic">resultObjPtr</emph> is not NULL, it will be used verbatim to supply the result of <emph style="italic">interp</emph>. The <emph style="italic">clientData</emph> is reserved for future use and must be zero. This function may be called from any thread in the process, regardless of which thread created <emph style="italic">interp</emph>.</para>
<quote>int <emph style="bold">Tcl_Canceled</emph>(Tcl_Interp *<emph style="italic">interp</emph>, int <emph style="italic">flags</emph>)</quote>
<para>The <emph style="bold">Tcl_Canceled</emph> function checks whether the script being evaluated in <emph style="italic">interp</emph> has been canceled. Returns a standard Tcl result (i.e., <emph style="italic">TCL_ERROR</emph> if the script being evaluated has been canceled). This function should only be called from the thread which created <emph style="italic">interp</emph>; otherwise, its behavior is undefined.</para>
</subsection>
</section>
<section title="Reference Implementation">
<para>A reference implementation of this TIP is available [<url ref="http://sf.net/tracker/?func=detail&amp;aid=1499394&amp;group_id=10894&amp;atid=310894"/>].</para>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
</section>
</body></TIP>

