TIP:		68
Title:		Dynamic Trace Result Handling
Version:	$Revision: 1.5 $
Author:		Donal K. Fellows <fellowsd@cs.man.ac.uk>
State:		Final
Type:		Project
Tcl-Version:	8.4
Vote:		Done
Created:	16-Oct-2001
Post-History:	

~ Abstract

This TIP proposes an extension to the ''Tcl_TraceVar'' API to cope
with dynamically allocated results.

~ Rationale

The current API for handling errors during variable accesses is based
on static strings, which is perfectly adequate for the errors that Tcl
generates of its own accord, but which is substantially at odds with
the setting of traces on variables which may produce errors.  The
problem is that as those errors come from a Tcl script, they are
allocated dynamically and fail to satisfy the static allocation rule
mentioned previously.  Normally this does not cause a problem, but
under some circumstances (as set out in Bug #219393
http://sf.net/tracker/?func=detail&aid=219393&group_id=10894&atid=110894)
it is possible for this to cause a memory fault or even memory
corruption.  This is because it can sometimes happen that the pointer
to the supposedly static string winds up dangling as the string it was
pointing to gets deleted out from underneath it (the storage area used
to static-ify the string is part of the trace structure, but the trace
is permitted to delete that structure...)  Obviously this is not
desirable!

There are several possible fixes, but the two main ones are to:

 1. use the ''Tcl_Preserve'' mechanism to postpone deletion of the
    allocated memory block until it has been copied into something
    more permanent.

 2. add special handling so as to mark the error result coming back
    from the trace mechanism as something other than a static string.
    The main alternatives here are:

 > * A dynamically-allocated C string, to be disposed of with
     ''ckfree''.

 > * A dynamically-allocated ''Tcl_Obj'' reference, to be disposed of
     with a single call to ''Tcl_DecrRefCount''.

Although option 1 is the easiest to implement, it has the disadvantage
of putting a new ''non-obvious'' requirement on all variable traces,
and that is that their results are all ''Tcl_Preserve''d before the
end of the trace.  This is feasible for the Tcl core, but unreasonable
to ask of extension writers.

Instead I prefer option 2, and it is possible to introduce this change
in such a way that existing software does not see an API change (i.e.
there are no serious backward-compatibility issues) and both styles of
result listed above are supported.  The advantage of supporting both
of these is that dynamically allocated strings are a very easy
interface for extension writers to use though not particularly
efficient, and objects are a very efficient interface well-suited to
the core itself but are not as easy to use.  (It is far easier to
adapt existing code to use dynamic strings as no understanding of
lifespan management is required.)

~ Changes

To achieve this, the following new flags will be defined:

|#define TCL_TRACE_RESULT_DYNAMIC  0x2000
|#define TCL_TRACE_RESULT_OBJECT   0x4000

These flags, when passed to the ''flags'' argument of ''Tcl_TraceVar''
(and related functions) alter the interpretation of the value returned
by the call to the ''proc'' parameter from the default behaviour (a
static string) to be either a string to be deallocated by Tcl as and
when it sees fit using ''ckfree'' (when ''TCL_TRACE_RESULT_DYNAMIC''
is specified) or to be a ''Tcl_Obj'' (which must be cast to a ''char
*'' for type compatibility) to be disposed of when the error message
is no longer required (when ''TCL_TRACE_RESULT_OBJECT'' is specified.)
It is an error to specify both flags on the same call.

The core will then be modified to use this mechanism for variable
traces as set up by the ''trace'' command.

~ Copyright

This TIP is placed in the public domain.

~ Reference

For reference, the pre-TIP definition of the ''Tcl_TraceVar'' function
is as follows:

|     int
|     Tcl_TraceVar(Tcl_Interp *interp, char *varName, int flags,
|                  Tcl_VarTraceProc *proc, ClientData clientData)

(There is a corresponding function that takes the variable name as a
pair of strings.)  All parameters have the usual obvious
interpretations, with the ''flags'' being an OR-ed combination of the
following flags:

  TCL_TRACE_READS: Invoke the callback when the variable is read.

  TCL_TRACE_WRITES: Invoke the callback when the variable is written.

  TCL_TRACE_UNSETS: Invoke the callback when the variable is unset.

  TCL_TRACE_ARRAY: Invoke the callback when the variable is accessed
     as an array.

  TCL_GLOBAL_ONLY: Force the lookup of the variable in the global
     scope, and not the current one.

