On 10/7/06, Zoran Vasiljevic <[EMAIL PROTECTED]> wrote:
Stephen, this is what I got on Tcl core:

Begin forwarded message:

> From: miguel sofer <[EMAIL PROTECTED]>
> Date: 7. Oktober 2006 16:30:05 MESZ
> To: Zoran Vasiljevic <[EMAIL PROTECTED]>
> Cc: miguel <[EMAIL PROTECTED]>, Tcl List Core <tcl-
> [EMAIL PROTECTED]>
> Subject: Re: [TCLCORE] Lifetime of a literal object
> Reply-To: [EMAIL PROTECTED]
>
> Zoran Vasiljevic wrote:
>> On 07.10.2006, at 16:05, Zoran Vasiljevic wrote:
>>
>>> Ah... that's what I was afaid of...
>>> Actually, I wanted to use tne internal rep of that object to
>>> store (or better cache) some expensivley calculated value.
>>> I was hoping to have this object "alive" for the time my
>>> predure is defined at least or (better) until the interp
>>> which house it is destroyed.
>>
>> To be more precise, I wanted to cheat...
>>
>> Consider:
>>
>>    proc foo args {
>>       puts baz
>>       cache baz
>>       return
>>    }
>>    proc humpty args {
>>       puts dumpty
>>       cache dumpty
>>       return
>>    }
>>
>> Now the [cache] above is my hypothetical command
>> which calculates some arbitrary value and splices
>> it in the object internal rep but w/o invalidating
>> its string rep.
>> It does that ONLY if the current internal rep of the
>> (literal) object does not already contain precalculated
>> value.
>>
>> What I hope to achieve with that is: only the first time
>> when I call the procedure my [cache] will actually have
>> to do the calculation. All other invocations will use
>> pre-calculated value stored in the "baz" or "dumpty"
>> literals.
>> So this way I could "re-use" literals as poor man's cache.
>>
>> Would that work?
>
> For a while ... but I wouldn't. Such a misuse of Tcl_Objs and EIAS may
> lead to weird bugs.
>
> I encourage you to talk with Jean-Claude Wippler about these issues;
> he's been looking for workarounds for ages. If you guys do find a good
> way to do things, share it!
>
> Refs that may be interesting:
> http://aspn.activestate.com/ASPN/Mail/Message/3168511
> https://sourceforge.net/tracker/index.php?
> func=detail&aid=1512138&group_id=10894&atid=360894
> http://aspn.activestate.com/ASPN/Mail/Message/tcl-core/3168677
>


Thanks for looking into this!

I think Miguel has the wrong end of the stick. Jean Claude is trying
to do the opposite of what I am -- he wants to change the string
representation without changing the internal rep. That's weird because
the string rep is the canonical rep.  "1" can be a string, or it can
be an int. The string rep is the same, but depending on which command
you pass it to, the internal rep can change.  The internal rep is a
private implementation detail of each command.

What I'm trying to do is change the internal rep without invalidating
the string rep.  That's slightly weird as the usual reason for
changing the internal rep is because the notion of the object value
has changed, such as from 1 to 2, and therefore the string rep also
needs to change (although lazily, so we just invalidate it).

However, it's not *that* weird. I copied the idea straight from the Tcl core!

Consider the Tcl_GetIndexFromObj() function:


/*
*----------------------------------------------------------------------
*
* Tcl_GetIndexFromObj --
*
*      This function looks up an object's value in a table of strings and
*      returns the index of the matching string, if any.
*
* Results:
*      ...
*
* Side effects:
 *      The result of the lookup is cached as the internal rep of objPtr, so
 *      that repeated lookups can be done quickly.
*
*----------------------------------------------------------------------
*/


the result of the lookup is cached in the internal rep.  That's
exactly what I'm doing. Here's how it happens
(Tcl_GetIndexFromObjStruct):

 done:
   /*
    * Cache the found representation. Note that we want to avoid allocating a
    * new internal-rep if at all possible since that is potentially a slow
    * operation.
    */

   if (objPtr->typePtr == &indexType) {
       indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
   } else {
       TclFreeIntRep(objPtr);
       indexRep = (IndexRep *) ckalloc(sizeof(IndexRep));
       objPtr->internalRep.otherValuePtr = (void *) indexRep;
       objPtr->typePtr = &indexType;
   }

The important bit is the 'else' arm. Notice how the internal rep of
the object is changed, but the string rep is not invalidated. (Neither
is it's ref count checked.)

This is all fine as far as I can see.  It doesn't break Tcl's COW
semantics for objects, which is concerned with the *value* of the
object, not the implementation details of how that is stored.

But we've learnt that Tcl likes to put literals in an object cache and
pass around a single, shared object. So what happens in the following
case?

   proc foo args {
       ns_job eval -timeout 5 -- ...
       ns_proxy eval -timeout 5 -- ...
   }

Both '-timeout' and 'eval' are literals and will end up in the literal
cache when the foo proc is byte code compiled.

The recommended way to parse sub-commands is with
Tcl_GetIndexFromObj(). The first time it is called for the ns_job
command it will cache the result of it's lookup in the 'eval' literal.
The next lookup should be fast.

But the shared 'eval' object is than passed to ns_proxy and when it
runs Tcl_TclGetIndexFromObj() a new result will be cached as the
internal rep.

Decoding sub-commands will never be quick as shared literals will be
shimmering back and forth between one cached value and another.  It
will be extremely common to have sub commands and switches with the
same name.


This seems completely broken.  What am I missing?

Reply via email to