Tcl_ObjSetVar2 ...

52 views
Skip to first unread message

MartinLemburg@UGS

unread,
Oct 12, 2005, 6:35:53 AM10/12/05
to
Hello,

may be I overread it, but the documentation of Tcl_ObjSetVar2 does not
mention that the Tcl_Obj used as value must not be deallocated by
Tcl_DecrRefCount, because tcl takes over the control over this Tcl_Obj.

I had GFPs because I did the following:

| Tcl_Obj *array, *element, *value;
|
| array = Tcl_NewStringObj( "array", -1 );
| element = Tcl_NewStringObj( "element", -1 );
| value = Tcl_NewStringObj( "value", -1 );
|
| Tcl_ObjSetVar2( ip, array, element, value, TCL_GLOBAL_ONLY );
|
| Tcl_DecrRefCount( value ); // location of the GFP

But if the result of Tcl_ObjSetVar2 is the same pointer than stored in
"value", than value seems to be taken over by tcl, right?

What's about being more specific inside the documentation?

Now I changed my source to do:

| Tcl_Obj *array, *element, *value;
|
| array = Tcl_NewStringObj( "array", -1 );
| element = Tcl_NewStringObj( "element", -1 );
| value = Tcl_NewStringObj( "value", -1 );
|
| Tcl_IncrRefCount( array );
| Tcl_IncrRefCount( element );
| Tcl_IncrRefCount( value );
|
| Tcl_ObjSetVar2( ip, array, element, value, TCL_GLOBAL_ONLY );
|
| Tcl_DecrRefCount( array );
| Tcl_DecrRefCount( element );
| Tcl_DecrRefCount( value );

In my eyes this is the cleanest way to handle this, right?

Ok - problem solved, but ... this was an unnecessary problem.

Best Regards,

Martin

miguel sofer

unread,
Oct 12, 2005, 8:12:33 AM10/12/05
to
The *only* always-safe way to work with Tcl_Objs is: Tcl_IncrRefCount()
on creation (before passing it to any function in the Tcl library) and
Tcl_DecrRefCount when you are done with it. This is what you are doing
in the second version of your code.

When you know what the particular Tcl function is doing with your
object, it may be safe to omit doing one or the other (the core does
this a lot). Except for critical performance considerations, I
recommend avoiding this.

Miguel

David N. Welton

unread,
Oct 12, 2005, 3:36:27 PM10/12/05
to MartinLemburg@UGS
MartinLemburg@UGS wrote:

> | Tcl_IncrRefCount( array );
> | Tcl_IncrRefCount( element );
> | Tcl_IncrRefCount( value );
> |
> | Tcl_ObjSetVar2( ip, array, element, value, TCL_GLOBAL_ONLY );
> |
> | Tcl_DecrRefCount( array );
> | Tcl_DecrRefCount( element );
> | Tcl_DecrRefCount( value );
>
> In my eyes this is the cleanest way to handle this, right?
>
> Ok - problem solved, but ... this was an unnecessary problem.

It's been a bit since I've looked at the C stuff, so I'm a bit rusty,
but I believe you are indeed correct in that you have to Incr it first,
then Decr it.

Ciao,
--
David N. Welton
- http://www.dedasys.com/davidw/

Linux, Open Source Consulting
- http://www.dedasys.com/

Kevin Kenny

unread,
Oct 17, 2005, 11:47:58 AM10/17/05
to
MartinLemburg@UGS wrote:
> Now I changed my source to do:
>
> | Tcl_Obj *array, *element, *value;
> |
> | array = Tcl_NewStringObj( "array", -1 );
> | element = Tcl_NewStringObj( "element", -1 );
> | value = Tcl_NewStringObj( "value", -1 );
> |
> | Tcl_IncrRefCount( array );
> | Tcl_IncrRefCount( element );
> | Tcl_IncrRefCount( value );
> |
> | Tcl_ObjSetVar2( ip, array, element, value, TCL_GLOBAL_ONLY );
> |
> | Tcl_DecrRefCount( array );
> | Tcl_DecrRefCount( element );
> | Tcl_DecrRefCount( value );
>
> In my eyes this is the cleanest way to handle this, right?
>
> Ok - problem solved, but ... this was an unnecessary problem.

Your initial problem was the misconception that Tcl objects
begin their lives with a reference count of 1. They start
out with a reference count of 0. Your original code created
the objects, called Tcl_ObjSetVar2, and then decremented the
ref counts. Anywhere that you unbalance a ref count like that
is a recipe for a pointer smash.

Another point is that you're choosing an unnecessarily complex
way of setting a variable. Variable names and array keys are
strings, not objects, internally, and so there's no advantage
to Tcl_Objifying them first.

So I'd begin:

Tcl_Obj* value;


value = Tcl_NewStringObj( "value", -1 );

Tcl_IncrRefCount( value );
Tcl_SetVar2Ex( interp, "array", "element", value, TCL_GLOBAL_ONLY );
Tcl_DecrRefCount( value );

Next (for experts only), I'd recognize that Tcl_SetVar2Ex is
safe with a zero-ref object for its 'value' argument, and
eliminate the refcount management altogether:

Tcl_SetVar2Ex( interp, "array", "element",
Tcl_NewStringObj( "value", -1 ), TCL_GLOBAL_ONLY );

I agree that we *ought* at least to document which API routines
are safe for zero-ref objects, but frankly it isn't my highest
priority. Experience suggests that by the time Tcl-C programmers
really "get" reference counting, they're reading the source
anyway.

--
73 de ke9tv/2, Kevin

Eckhard Lehmann

unread,
Oct 17, 2005, 2:26:26 PM10/17/05
to
Kevin Kenny wrote:

> priority. Experience suggests that by the time Tcl-C programmers
> really "get" reference counting, they're reading the source
> anyway.

Nice statement.
I do read the source - since I am interrested. But this is definitely
*not* the way to find out how an API works, in terms of productivity and
elegance. It is sooo 70's...


Eckhard

miguel sofer

unread,
Oct 17, 2005, 2:58:16 PM10/17/05
to
A contrarian's view: I do not agree with Kevin, we should *not*
document which API routines are safe for zero-ref objects. Documenting
is too close to committing to freeze at that state.

The only officialy sanctioned way to deal with Tcl_Objs is:
IncrRefCount before passing to any function in the library,


DecrRefCount when you are done with it.

Anybody wanting to live on the edge in order to scrape a few runtime
microseconds ought to
(a) read the source
(b) be prepared to deal with code breakage even at dot releases

The core can do a lot of things that are dangerous for extensions, and
this is one of those. Extensions that include tclInt.h or are otherwise
too intimate with the core's implementation details require (a) and (b)
above from their authors. And keep getting into upgrade trouble.

Miguel

Don Porter

unread,
Oct 17, 2005, 4:45:35 PM10/17/05
to
miguel sofer wrote:
> The only officialy sanctioned way to deal with Tcl_Objs is:
> IncrRefCount before passing to any function in the library,
> DecrRefCount when you are done with it.

It's stricter than that, though.

You must IncrRefCount before calling any Tcl_* routine (or calling
anything that calls a Tcl_* routine, and so on) that might
conceivably decrement the ref count of the Tcl_Obj of interest.

Given the number of traces and callbacks that are possible,
that's a lot of IncrRefCount-ing to be "reliably safe".

--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|

Joe English

unread,
Oct 17, 2005, 10:25:49 PM10/17/05
to
miguel sofer wrote:

>A contrarian's view: I do not agree with Kevin, we should *not*
>document which API routines are safe for zero-ref objects. Documenting
>is too close to committing to freeze at that state.
>
>The only officialy sanctioned way to deal with Tcl_Objs is:
>IncrRefCount before passing to any function in the library,
>DecrRefCount when you are done with it.

* * * DANGER DANGER DANGER * * *

Do not follow the above advice literally! You will get into trouble!


The first problem is that if you Tcl_IncrRefCount() an object too many
times before passing it to a mutator, it will make the program panic.

The second problem is that if you increment and decrement the refcount
of a Tcl_Obj that was passed to you from somewhere else, then the object
will vanish -- possibly before you want it too -- if you're ever passed
a fresh object.

Since some routines can't be passed shared objects, and other
routines can't be passed *fresh* objects, it's important to
know the refcount effects of all library routines. This could
be better documented, and in some cases really does need to
be frozen.

I don't have any better rules of thumb to offer, but a start:

There are roughly four classes of Tcl_Obj-related library routines:

+ Constructors, which return a fresh Tcl_Obj with 0 refcount;

+ Readers, which only read the value (but may cause shimmering).

+ Consumers, which store a new reference to an existing Tcl_Obj,
increment the refcount, and arrange to decremement the
refcount at some unspecified point in the future.

+ Mutators, which change the Tcl_Obj and can *only* be used
with unshared Tcl_Objs (refcount = 0 or 1).


--Joe English

MartinLemburg@UGS

unread,
Oct 18, 2005, 3:31:32 AM10/18/05
to
Thanks for this reply Joe!

I wrote for our application a library with a lot of tcl helper
functions and I often ran into trouble with incrementing objects by
rule! And I often ran into trouble by not incrementing objects!

My experiences tell me that your list of Constructors, Readers, etc. is
right, but it is difficult to get the right way to work with the
reference counters, because the documentation has only rarely hints or
comments on this.

The question about the Tcl_ObjSetVar2 usage came up, because it was the
first time I wanted to reuse the object I pushed into a variable!

I would be very glad, if the documentation would be much clearer about
using the reference count with the C API!

Best regards,

Martin Lemburg
UGS - Transforming the Process of Innovation

MartinLemburg@UGS

unread,
Oct 18, 2005, 3:48:19 AM10/18/05
to
Hi Kevin,

I think you are not always right with incrementing reference counts.

There are several C API functions not wanting shared objects!

And the Tcl_SetVar2Ex function was never used by me, because I had to
maintain code from 8.1 to 8.4 and it was necessary to use the
intersection of the APIs.
Now I am able to switch completely to 8.4 and so I will use
Tcl_SetVar2Ex in the future.

Thanks and best regards,

Donal K. Fellows

unread,
Oct 18, 2005, 4:25:31 AM10/18/05
to
Joe English wrote:
> There are roughly four classes of Tcl_Obj-related library routines:
>
> + Constructors, which return a fresh Tcl_Obj with 0 refcount;
>
> + Readers, which only read the value (but may cause shimmering).
>
> + Consumers, which store a new reference to an existing Tcl_Obj,
> increment the refcount, and arrange to decremement the
> refcount at some unspecified point in the future.
>
> + Mutators, which change the Tcl_Obj and can *only* be used
> with unshared Tcl_Objs (refcount = 0 or 1).

There's one other class of object function:

+ Hairy Monsters. Don't give these things refcount==0 objects since
they will manipulate the refcount during their processing and might
or might not retain a reference.

Luckily, only the virtual filesystem and Tcl_EvalObjEx() come into this
category, and when the object being used is an argument passed to you or
a value read from a variable, you're naturally OK anyway. (I think.)

Donal.

miguel sofer

unread,
Oct 18, 2005, 6:05:14 AM10/18/05
to
Oops - that rule was too hasty. Sorry. Let me try again.

Note however that it only fails to deal with mutators: constructors are
outside the scope (as there is no obj before you call them), and it
deals safely with readers, consumers and "hairy monsters". Even
mutators are handled correctly for freshly created objects - the
scenario of the original question, and which should have been explicit
for such an attempt at a "general rule".

Mutators are indeed a special case. You can only call mutators on
objects that you "own", in order to respect copy-on-write (COW)
semantics. If you are not writing a mutator yourself, the safe rule
should be complemented with "make a copy of the object for yourself
before calling a mutator on the copy". And you should apply the
previous rule to the freshly created object.

If your function is itself a mutator, then it should panic on receiving
a shared object: your caller is violating COW. If everybody is
following the general rule, the obj you received has refCount 1
(maintained by your caller) and you need not fear the effect of calling
a "hairy monster" on them.

On second thoughts, the original rule is not that bad: it only fails to
deal with the case where your trouble is not really with refCounts, but
rather with the COW semantics of the language.

AFAIK, mutators *are* always documented as such. If they're not, that
is indeed a doc bug.

miguel sofer

unread,
Oct 18, 2005, 6:18:30 AM10/18/05
to
> ... and when the object being used is an argument passed to you or

> a value read from a variable, you're naturally OK anyway. (I think.)

Assuming your caller insures a refCount >=1 (as it should if it is
following the rules), you are indeed safe when dealing with arguments.
But what if the caller misshandled the refCounts? If you're better safe
than sorry, you'll follow the general rule.

OTOH, if the obj is a variable's value: what if a trace unsets the
variable as a result of your call into the library? Ah, better qualify
that: you are only safe if you call functions that cannot modify
variables. Be mindful of traces, fileevents if someone might call
[update] on you, and also ... Hey, know what? I'd rather follow the
general rule :)

(Note: general rule as clarified in my reply to jenglish - which I hope
to have phrased correctly now).

Kevin Kenny

unread,
Oct 18, 2005, 6:50:55 AM10/18/05
to
Joe English wrote:
> There are roughly four classes of Tcl_Obj-related library routines:
>
> + Constructors, which return a fresh Tcl_Obj with 0 refcount;
>
> + Readers, which only read the value (but may cause shimmering).
>
> + Consumers, which store a new reference to an existing Tcl_Obj,
> increment the refcount, and arrange to decremement the
> refcount at some unspecified point in the future.
>
> + Mutators, which change the Tcl_Obj and can *only* be used
> with unshared Tcl_Objs (refcount = 0 or 1).

And a fifth:

+ Rogues, which appear to be readers, but call consumers somewhere
internally and are hence unsafe for 0-ref objects.

Donal K. Fellows

unread,
Oct 18, 2005, 8:18:02 AM10/18/05
to
> But what if the caller misshandled the refCounts?

That's called a Bug. :-D

Donal.

miguel sofer

unread,
Oct 22, 2005, 8:07:51 PM10/22/05
to
In light of Bug #1334947, I think I am reviewing my position. If at all
possible, we should try to:
- insure that "consumers" actually do consume the Tcl_Obj in all paths
(some fail to do so in case of errors), in the sense that the caller
may forget about the Tcl_Obj completely
- shave all hairy monsters
- improve the documentation

Even so, the rule stands: in case of doubt, incr before sending and
decr on return is always the safest thing to do when not calling
mutators.

miguel sofer

unread,
Oct 24, 2005, 8:09:34 AM10/24/05
to
I started a wiki page on this issue: http://wiki.tcl.tk/14880

Reply all
Reply to author
Forward
0 new messages