u_dynarray: return 0 on realloc failure and ensure no-op
authorNicolai Hähnle <nicolai.haehnle@amd.com>
Mon, 13 May 2019 14:58:07 +0000 (16:58 +0200)
committerMarek Olšák <marek.olsak@amd.com>
Wed, 12 Jun 2019 22:30:25 +0000 (18:30 -0400)
We're not very good at handling out-of-memory conditions in general, but
this change at least gives the caller the option of handling it gracefully
and without memory leaks.

This happens to fix an error in out-of-memory handling in i965, which has
the following code in brw_bufmgr.c:

      node = util_dynarray_grow(vma_list, sizeof(struct vma_bucket_node));
      if (unlikely(!node))
         return 0ull;

Previously, allocation failure for util_dynarray_grow wouldn't actually
return NULL when the dynarray was previously non-empty.

v2:
- make util_dynarray_ensure_cap a no-op on failure, add MUST_CHECK attribute
- simplify the new capacity calculation: aside from avoiding a useless loop
  when newcap is very large, this also avoids an infinite loop when newcap
  is larger than 1 << 31

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
src/util/u_dynarray.h

index b30fd7b..769c382 100644 (file)
@@ -77,21 +77,23 @@ util_dynarray_clear(struct util_dynarray *buf)
 
 #define DYN_ARRAY_INITIAL_SIZE 64
 
-static inline void *
+MUST_CHECK static inline void *
 util_dynarray_ensure_cap(struct util_dynarray *buf, unsigned newcap)
 {
    if (newcap > buf->capacity) {
-      if (buf->capacity == 0)
-         buf->capacity = DYN_ARRAY_INITIAL_SIZE;
-
-      while (newcap > buf->capacity)
-         buf->capacity *= 2;
+      unsigned capacity = MAX3(DYN_ARRAY_INITIAL_SIZE, buf->capacity * 2, newcap);
+      void *data;
 
       if (buf->mem_ctx) {
-         buf->data = reralloc_size(buf->mem_ctx, buf->data, buf->capacity);
+         data = reralloc_size(buf->mem_ctx, buf->data, capacity);
       } else {
-         buf->data = realloc(buf->data, buf->capacity);
+         data = realloc(buf->data, capacity);
       }
+      if (!data)
+         return 0;
+
+      buf->data = data;
+      buf->capacity = capacity;
    }
 
    return (void *)((char *)buf->data + buf->size);