X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftalloc%2Ftalloc.c;fp=ccan%2Ftalloc%2Ftalloc.c;h=63423a50f5ec05e73aa1816910fdc82860fbb2d0;hp=fdf8eebed297db56d26abdca812b515702ffdf02;hb=1e962ba9b5808dafc0be9cc562b25e3ae068ca5d;hpb=87aba1fa2abc28ff06f82e30cde3fa3d991e8e18 diff --git a/ccan/talloc/talloc.c b/ccan/talloc/talloc.c index fdf8eebe..63423a50 100644 --- a/ccan/talloc/talloc.c +++ b/ccan/talloc/talloc.c @@ -538,13 +538,28 @@ static inline int _talloc_free(const void *ptr) final choice is the null context. */ void *child = TC_PTR_FROM_CHUNK(tc->child); const void *new_parent = null_context; + struct talloc_chunk *old_parent = NULL; if (unlikely(tc->child->refs)) { struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } + /* finding the parent here is potentially quite + expensive, but the alternative, which is to change + talloc to always have a valid tc->parent pointer, + makes realloc more expensive where there are a + large number of children. + + The reason we need the parent pointer here is that + if _talloc_free_internal() fails due to references + or a failing destructor we need to re-parent, but + the free call can invalidate the prev pointer. + */ + if (new_parent == null_context && (tc->child->refs || tc->child->destructor)) { + old_parent = talloc_parent_chunk(ptr); + } if (unlikely(_talloc_free(child) == -1)) { if (new_parent == null_context) { - struct talloc_chunk *p = talloc_parent_chunk(ptr); + struct talloc_chunk *p = old_parent; if (p) new_parent = TC_PTR_FROM_CHUNK(p); } __talloc_steal(new_parent, child);