talloc quiz, and: dangling talloc references and inconsistencies in the talloc model

Sam Liddicott sam at liddicott.com
Thu Jan 22 10:42:35 GMT 2009


* tridge wrote, On 16/01/09 00:19:
> Hi Sam,
>
> First off, I should say that talloc_reference() is definately the
> trickiest part of talloc, and that I often try to avoid it. I've also
> been quite tempted to change talloc to fail explicit talloc_free()
> calls when a pointer has a reference and to require that
> talloc_unlink() be used in that case. An explicit talloc_free() just
> doesn't give enough information to the talloc library to always do the
> 'right thing'.
>
> That said, I don't agree with the proposed solution (though perhaps
> you can convince me). I deliberately chose the current semantics for a
> good reason.
>
> To keep it concrete, let's look at the example you added to the test
> suite:
>
> 	void *root, *p1, *p2, *ref, *r1;
>
> 	root = talloc_named_const(NULL, 0, "root");
> 	p1 = talloc_named_const(root, 1, "p1");
> 	p2 = talloc_named_const(p1, 1, "p2");
> 	/* Now root owns p1, and p1 owns p2 */
>
> 	r1 = talloc_named_const(root, 1, "r1");
> 	ref = talloc_reference(r1, p2);
>
> This is the setup you are concerned about. You now worry about the
> difference from the point of view of r1 between talloc_free(p1) and
> talloc_free(p2). I'd like to expand that to a 3rd case for you to
> consider, which is talloc_free(r1).
>
> What talloc is really trying to simulate with references is the
> ability for a pointer to have two parents. The only difference between
> the two parents of p2 is that one was established earlier than the
> other. The fact that internally talloc considers one to be an 'owner'
> and the other a 'reference' is supposed to be hidden as far as
> possible from the programmer. Unfortunately it isn't completely
> hidden.
>
> So from that point of view the memory tree looks like this:
>
>                            root
>                             / \
>                            /   \
>                           /     \
>                          /       \
>                         r1       p1
>                           \      / 
>                            \    /
>                             \  /
>                           (p2,ref)
>
> Notice that I've labelled the bottom pointer with two names. The value
> of p2 is guaranteed to be the same value as ref, so they are the same
> pointer. When p2 or ref is passed to a function we have no way to
> distinguish which is being used (as its the same value).
>
> So let's look at the 3 cases and try to work out the intent of the
> programmer in each case.
>
>   1) talloc_free(r1). The intent in this case is very clear. The
>   programmer is destroying the tree starting at r1, which means we
>   should end up with this:
>
>                            root
>                               \
>                                \
>                                 \
>                                  \
>                                  p1
>                                  / 
>                                 /
>                                /
>                           (p2,ref)
>
>   2) talloc_free(p1). The intent in this case is also clear in this
>   case. The programmer is destroying the tree starting at p1, which
>   means we should end up with this:
>
>                            root
>                             /  
>                            /    
>                           /      
>                          /        
>                         r1         
>                           \        
>                            \     
>                             \   
>                           (p2,ref)
>
>   3) talloc_free(p2). This is the tricky one. There is no way to
>   distinguish this from talloc_free(ref), so we have to choose one of
>   the two above approaches. The approach I chose in talloc was that
>   the most recent parent should be removed. This is because with no
>   way to distinguish what the programmer wanted I needed some
>   consistent rule to use, and that is the most logical rule I could
>   think of and it made sense to me in terms of the common nesting used
>   with references. 
I think this is a good assumption, but I think it is less likely to hold
true in a system of asynchronous modules (like vfs layers) when interest
in structures isn't necessarily nested, and where it is nearly
impossible to test whether or not a particular use will be safe at runtime.
> That gives us this:
>
>                            root
>                             / \
>                            /   \
>                           /     \
>                          /       \
>                         r1       p1
>                                  / 
>                                 /
>                                /
>                           (p2,ref)
>
>
> The test_implicit_explicit_free() test checks on something quite
> different, and ignores the "two parent" view of talloc references. I
> think it is looking for consistency in the wrong way, and ignores the
> fact that talloc_free(p2) is the same call as talloc_free(ref). 
I see why you say this. In fact it considers that talloc_free is unsafe
for the reasons you give above.
> It
> also increases the exposure to the programmer of the idea of who is
> the 'owner' of a pointer, thus reducing the illusion talloc tries to
> create of having a true multi-parent tree structure.
>
> btw, the 'quiz' questions you posted don't make any sense to me, as
> they don't explain that the functions save_for_later() and
> sneak_final_look() do. If you give some explicit, runnable, code then
> it would make more sense to me. Just saying "you got everything right"
> doesn't tell me what the code actually does. There could well be bugs
> in talloc, but I'd need test cases to confirm that it really is a bug.
>   

> Cheers, Tridge
>   




More information about the samba-technical mailing list