improve mempool destructor support (2/3)

Thu, 22 May 2025 21:00:33 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 22 May 2025 21:00:33 +0200
changeset 1324
399f7bb81d11
parent 1323
deccdb82f24e
child 1325
20caf6efaf07

improve mempool destructor support (2/3)

implement new behavior

issue #655

src/mempool.c file | annotate | diff | comparison | revisions
--- a/src/mempool.c	Thu May 22 16:25:32 2025 +0200
+++ b/src/mempool.c	Thu May 22 21:00:33 2025 +0200
@@ -186,6 +186,242 @@
     }
 }
 
+static cx_allocator_class cx_mempool_simple_allocator_class = {
+    cx_mempool_malloc_simple,
+    cx_mempool_realloc_simple,
+    cx_mempool_calloc_simple,
+    cx_mempool_free_simple
+};
+
+static void *cx_mempool_malloc_advanced(
+        void *p,
+        size_t n
+) {
+    struct cx_mempool_s *pool = p;
+
+    if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+        return NULL;
+    }
+
+    struct cx_mempool_memory2_s *mem =
+        cxMallocDefault(sizeof(struct cx_mempool_memory_s) + n);
+    if (mem == NULL) return NULL;
+    mem->destructor = NULL;
+    mem->data = NULL;
+    pool->data[pool->size] = mem;
+    pool->size++;
+
+    return mem->c;
+}
+
+static void *cx_mempool_calloc_advanced(
+        void *p,
+        size_t nelem,
+        size_t elsize
+) {
+    size_t msz;
+    if (cx_szmul(nelem, elsize, &msz)) {
+        errno = EOVERFLOW;
+        return NULL;
+    }
+    void *ptr = cx_mempool_malloc_advanced(p, msz);
+    if (ptr == NULL) return NULL;
+    memset(ptr, 0, nelem * elsize);
+    return ptr;
+}
+
+static void *cx_mempool_realloc_advanced(
+        void *p,
+        void *ptr,
+        size_t n
+) {
+    struct cx_mempool_s *pool = p;
+
+    const unsigned overhead = sizeof(struct cx_mempool_memory2_s);
+    struct cx_mempool_memory2_s *mem =
+        (void *) (((char *) ptr) - overhead);
+    struct cx_mempool_memory2_s *newm =
+        cxReallocDefault(mem, n + overhead);
+
+    if (newm == NULL) return NULL;
+    if (mem != newm) {
+        for (size_t i = 0; i < pool->size; i++) {
+            if (pool->data[i] == mem) {
+                pool->data[i] = newm;
+                return ((char*)newm) + overhead;
+            }
+        }
+        abort(); // LCOV_EXCL_LINE
+    } else {
+        return ptr;
+    }
+}
+
+static void cx_mempool_free_advanced(
+        void *p,
+        void *ptr
+) {
+    if (!ptr) return;
+    struct cx_mempool_s *pool = p;
+
+    struct cx_mempool_memory2_s *mem =
+        (void*) ((char *) ptr - sizeof(struct cx_mempool_memory2_s));
+
+    for (size_t i = 0; i < pool->size; i++) {
+        if (mem == pool->data[i]) {
+            if (mem->destructor) {
+                mem->destructor(mem->data, mem->c);
+            }
+            if (pool->destr) {
+                pool->destr(mem->c);
+            }
+            if (pool->destr2) {
+                pool->destr2(pool->destr2_data, mem->c);
+            }
+            cxFreeDefault(mem);
+            size_t last_index = pool->size - 1;
+            if (i != last_index) {
+                pool->data[i] = pool->data[last_index];
+                pool->data[last_index] = NULL;
+            }
+            pool->size--;
+            return;
+        }
+    }
+    abort(); // LCOV_EXCL_LINE
+}
+
+static void cx_mempool_free_all_advanced(const struct cx_mempool_s *pool) {
+    const bool has_destr = pool->destr;
+    const bool has_destr2 = pool->destr2;
+    for (size_t i = 0; i < pool->size; i++) {
+        struct cx_mempool_memory2_s *mem = pool->data[i];
+        if (mem->destructor) {
+            mem->destructor(mem->data, mem->c);
+        }
+        if (has_destr) {
+            pool->destr(mem->c);
+        }
+        if (has_destr2) {
+            pool->destr2(pool->destr2_data, mem->c);
+        }
+        cxFreeDefault(mem);
+    }
+}
+
+static cx_allocator_class cx_mempool_advanced_allocator_class = {
+    cx_mempool_malloc_advanced,
+    cx_mempool_realloc_advanced,
+    cx_mempool_calloc_advanced,
+    cx_mempool_free_advanced
+};
+
+
+static void *cx_mempool_malloc_pure(
+        void *p,
+        size_t n
+) {
+    struct cx_mempool_s *pool = p;
+
+    if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+        return NULL;
+    }
+
+    void *mem = cxMallocDefault(n);
+    if (mem == NULL) return NULL;
+    pool->data[pool->size] = mem;
+    pool->size++;
+
+    return mem;
+}
+
+static void *cx_mempool_calloc_pure(
+        void *p,
+        size_t nelem,
+        size_t elsize
+) {
+    size_t msz;
+    if (cx_szmul(nelem, elsize, &msz)) {
+        errno = EOVERFLOW;
+        return NULL;
+    }
+    void *ptr = cx_mempool_malloc_pure(p, msz);
+    if (ptr == NULL) return NULL;
+    memset(ptr, 0, nelem * elsize);
+    return ptr;
+}
+
+static void *cx_mempool_realloc_pure(
+        void *p,
+        void *ptr,
+        size_t n
+) {
+    struct cx_mempool_s *pool = p;
+    void *newm = cxReallocDefault(ptr, n);
+    if (newm == NULL) return NULL;
+    if (ptr != newm) {
+        for (size_t i = 0; i < pool->size; i++) {
+            if (pool->data[i] == ptr) {
+                pool->data[i] = newm;
+                return newm;
+            }
+        }
+        abort(); // LCOV_EXCL_LINE
+    } else {
+        return ptr;
+    }
+}
+
+static void cx_mempool_free_pure(
+        void *p,
+        void *ptr
+) {
+    if (!ptr) return;
+    struct cx_mempool_s *pool = p;
+
+    for (size_t i = 0; i < pool->size; i++) {
+        if (ptr == pool->data[i]) {
+            if (pool->destr) {
+                pool->destr(ptr);
+            }
+            if (pool->destr2) {
+                pool->destr2(pool->destr2_data, ptr);
+            }
+            cxFreeDefault(ptr);
+            size_t last_index = pool->size - 1;
+            if (i != last_index) {
+                pool->data[i] = pool->data[last_index];
+                pool->data[last_index] = NULL;
+            }
+            pool->size--;
+            return;
+        }
+    }
+    abort(); // LCOV_EXCL_LINE
+}
+
+static void cx_mempool_free_all_pure(const struct cx_mempool_s *pool) {
+    const bool has_destr = pool->destr;
+    const bool has_destr2 = pool->destr2;
+    for (size_t i = 0; i < pool->size; i++) {
+        void *mem = pool->data[i];
+        if (has_destr) {
+            pool->destr(mem);
+        }
+        if (has_destr2) {
+            pool->destr2(pool->destr2_data, mem);
+        }
+        cxFreeDefault(mem);
+    }
+}
+
+static cx_allocator_class cx_mempool_pure_allocator_class = {
+    cx_mempool_malloc_pure,
+    cx_mempool_realloc_pure,
+    cx_mempool_calloc_pure,
+    cx_mempool_free_pure
+};
+
 static void cx_mempool_free_foreign(const struct cx_mempool_s *pool) {
     for (size_t i = 0; i < pool->registered_size; i++) {
         struct cx_mempool_foreign_memory_s info = pool->registered[i];
@@ -199,19 +435,14 @@
     }
 }
 
-static cx_allocator_class cx_mempool_simple_allocator_class = {
-    cx_mempool_malloc_simple,
-    cx_mempool_realloc_simple,
-    cx_mempool_calloc_simple,
-    cx_mempool_free_simple
-};
-
 void cxMempoolFree(CxMempool *pool) {
     if (pool == NULL) return;
     if (pool->allocator->cl == &cx_mempool_simple_allocator_class) {
         cx_mempool_free_all_simple(pool);
+    } else if (pool->allocator->cl == &cx_mempool_advanced_allocator_class) {
+        cx_mempool_free_all_advanced(pool);
     } else {
-        // TODO: implement
+        cx_mempool_free_all_pure(pool);
     }
     cx_mempool_free_foreign(pool);
     cxFreeDefault(pool->data);
@@ -227,10 +458,28 @@
     *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
 }
 
+void cxMempoolSetDestructor2(
+        void *ptr,
+        cx_destructor_func2 func,
+        void *data
+) {
+    struct cx_mempool_memory2_s *info =
+        (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s));
+    info->destructor = func;
+    info->data = data;
+}
+
 void cxMempoolRemoveDestructor(void *ptr) {
     *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL;
 }
 
+void cxMempoolRemoveDestructor2(void *ptr) {
+    struct cx_mempool_memory2_s *info =
+        (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s));
+    info->destructor = NULL;
+    info->data = NULL;
+}
+
 int cxMempoolRegister(
         CxMempool *pool,
         void *memory,
@@ -250,6 +499,26 @@
     return 0;
 }
 
+int cxMempoolRegister2(
+        CxMempool *pool,
+        void *memory,
+        cx_destructor_func2 destr,
+        void *data
+) {
+    if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) {
+        return 1; // LCOV_EXCL_LINE
+    }
+
+    pool->registered[pool->registered_size++] =
+        (struct cx_mempool_foreign_memory_s) {
+            .mem = memory,
+            .destr2 = destr,
+            .destr2_data = data
+        };
+
+    return 0;
+}
+
 CxMempool *cxMempoolCreate(
         size_t capacity,
         enum cx_mempool_type type
@@ -277,9 +546,9 @@
     if (type == CX_MEMPOOL_TYPE_SIMPLE) {
         provided_allocator->cl = &cx_mempool_simple_allocator_class;
     } else if (type == CX_MEMPOOL_TYPE_ADVANCED) {
-        // TODO: implement
+        provided_allocator->cl = &cx_mempool_advanced_allocator_class;
     } else {
-        // TODO: implement
+        provided_allocator->cl = &cx_mempool_pure_allocator_class;
     }
 
     pool->data = cxMallocDefault(poolsize);

mercurial