return from_tal_hdr(group->parent_child->parent);
}
-void *tal_realloc_(tal_t *ctx, size_t size)
+bool tal_resize_(tal_t **ctxp, size_t size)
{
struct tal_hdr *old_t, *t, **prev;
struct group *group;
struct children *child;
- old_t = debug_tal(to_tal_hdr(ctx));
+ old_t = debug_tal(to_tal_hdr(*ctxp));
t = resizefn(old_t, size + sizeof(struct tal_hdr));
if (!t) {
call_error("Reallocation failure");
- tal_free(old_t);
- return NULL;
+ return false;
}
+
+ /* If it didn't move, we're done! */
if (t == old_t)
- return ctx;
+ return true;
update_bounds(t);
/* Fix up linked list pointer. */
assert(child->parent == old_t);
child->parent = t;
}
-
- return from_tal_hdr(debug_tal(t));
+ *ctxp = from_tal_hdr(debug_tal(t));
+ return true;
}
char *tal_strdup(const tal_t *ctx, const char *p)
if (ret < max)
break;
- buf = tal_resize(buf, max *= 2);
+ if (!tal_resize(&buf, max *= 2)) {
+ tal_free(buf);
+ buf = NULL;
+ }
}
if (ctx == TAL_TAKE)
tal_free(fmt);
* Allocates a specific type, with a given parent context. The name
* of the object is a string of the type, but if CCAN_TAL_DEBUG is
* defined it also contains the file and line which allocated it.
+ *
+ * Example:
+ * int *p = tal(NULL, int);
+ * *p = 1;
*/
#define tal(ctx, type) \
((type *)tal_alloc_((ctx), sizeof(type), false, TAL_LABEL(type, "")))
* @type: the type to allocate.
*
* Equivalent to tal() followed by memset() to zero.
+ *
+ * Example:
+ * p = talz(NULL, int);
+ * assert(*p == 0);
*/
#define talz(ctx, type) \
((type *)tal_alloc_((ctx), sizeof(type), true, TAL_LABEL(type, "")))
* children (recursively) before finally freeing the memory.
*
* Note: errno is preserved by this call.
+ *
+ * Example:
+ * tal_free(p);
*/
void tal_free(const tal_t *p);
* @ctx: NULL, or tal allocated object to be parent.
* @type: the type to allocate.
* @count: the number to allocate.
+ *
+ * Example:
+ * p = tal_arr(NULL, int, 2);
+ * p[0] = 0;
+ * p[1] = 1;
*/
#define tal_arr(ctx, type, count) \
((type *)tal_alloc_((ctx), tal_sizeof_(sizeof(type), (count)), false, \
* @ctx: NULL, or tal allocated object to be parent.
* @type: the type to allocate.
* @count: the number to allocate.
+ *
+ * Example:
+ * p = tal_arrz(NULL, int, 2);
+ * assert(p[0] == 0 && p[1] == 0);
*/
#define tal_arrz(ctx, type, count) \
((type *)tal_alloc_((ctx), tal_sizeof_(sizeof(type), (count)), true, \
TAL_LABEL(type, "[]")))
/**
- * tal_resize - enlarge or reduce a tal_arr(z).
- * @p: The tal allocated array to resize.
+ * tal_resize - enlarge or reduce a tal_arr[z].
+ * @p: A pointer to the tal allocated array to resize.
* @count: the number to allocate.
*
- * This returns the new pointer, or NULL (and destroys the old one)
- * on failure.
+ * This returns true on success (and may move *@p), or false on failure.
+ *
+ * Example:
+ * tal_resize(&p, 100);
*/
#define tal_resize(p, count) \
- ((tal_typeof(p) tal_realloc_((p), tal_sizeof_(sizeof(*p), (count)))))
+ tal_resize_((void **)(p), tal_sizeof_(sizeof**(p), (count)))
/**
* tal_steal - change the parent of a tal-allocated pointer.
* This may need to perform an allocation, in which case it may fail; thus
* it can return NULL, otherwise returns @ptr.
*
- * Weird macro avoids gcc's 'warning: value computed is not used'.
+ * Note: weird macro avoids gcc's 'warning: value computed is not used'.
*/
#if HAVE_STATEMENT_EXPR
#define tal_steal(ctx, ptr) \
tal_t *tal_steal_(const tal_t *new_parent, const tal_t *t);
-void *tal_realloc_(tal_t *ctx, size_t size);
+bool tal_resize_(tal_t **ctxp, size_t size);
bool tal_add_destructor_(tal_t *ctx, void (*destroy)(void *me));
return malloc(len);
}
+static void *failing_realloc(void *p, size_t len)
+{
+ if (alloc_count++ == when_to_fail)
+ return NULL;
+
+ return realloc(p, len);
+}
+
+
static void nofail_on_error(const char *msg)
{
diag("ERROR: %s", msg);
int main(void)
{
- void *p, *c1, *c2;
+ char *p, *c1, *c2;
bool success;
- plan_tests(21);
+ plan_tests(25);
- tal_set_backend(failing_alloc, NULL, NULL, nofail_on_error);
+ tal_set_backend(failing_alloc, failing_realloc, NULL, nofail_on_error);
/* Fail at each possible point in an allocation. */
when_to_fail = err_count = 0;
ok1(when_to_fail > 1);
ok1(err_count == when_to_fail - 1);
+ /* Now during resize. */
+ c2 = c1;
+ when_to_fail = err_count = 0;
+ for (;;) {
+ alloc_count = 0;
+ if (tal_resize(&c1, 100))
+ break;
+ /* Failing alloc will not change pointer. */
+ ok1(c1 == c2);
+ when_to_fail++;
+ };
+ ok1(alloc_count == 1);
+ ok1(when_to_fail == 1);
+ ok1(err_count == 1);
+ /* Make sure it's really resized. */
+ memset(c1, 1, 100);
+
/* Now for second child. */
when_to_fail = err_count = 0;
do {