]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add memarea API
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Fri, 8 Sep 2017 10:33:27 +0000 (13:33 +0300)
committerTimo Sirainen <tss@dovecot.fi>
Wed, 1 Nov 2017 11:45:51 +0000 (13:45 +0200)
This can be used to create reference counted memory areas where a callback
is called once the refcount drops to zero.

src/lib/Makefile.am
src/lib/memarea.c [new file with mode: 0644]
src/lib/memarea.h [new file with mode: 0644]
src/lib/test-lib.inc
src/lib/test-memarea.c [new file with mode: 0644]

index 949ce18480cceaa739949d20380de4fac5e48f91..b6d223168266967b5a4a1555c078179524cb560f 100644 (file)
@@ -97,6 +97,7 @@ liblib_la_SOURCES = \
        log-throttle.c \
        md4.c \
        md5.c \
+       memarea.c \
        mempool.c \
        mempool-alloconly.c \
        mempool-datastack.c \
@@ -248,6 +249,7 @@ headers = \
        md4.h \
        md5.h \
        malloc-overflow.h \
+       memarea.h \
        mempool.h \
        mkdir-parents.h \
        mmap-util.h \
@@ -364,6 +366,7 @@ test_lib_SOURCES = \
        test-llist.c \
        test-log-throttle.c \
        test-malloc-overflow.c \
+       test-memarea.c \
        test-mempool.c \
        test-mempool-alloconly.c \
        test-pkcs5.c \
diff --git a/src/lib/memarea.c b/src/lib/memarea.c
new file mode 100644 (file)
index 0000000..f4bf017
--- /dev/null
@@ -0,0 +1,93 @@
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "memarea.h"
+
+struct memarea {
+       const void *data;
+       size_t size;
+
+       memarea_free_callback_t *callback;
+       void *context;
+
+       int refcount;
+};
+
+static struct memarea memarea_empty = {
+       .refcount = 1,
+};
+
+#undef memarea_init
+struct memarea *
+memarea_init(const void *data, size_t size,
+            memarea_free_callback_t *callback, void *context)
+{
+       struct memarea *area;
+
+       i_assert(callback != NULL);
+
+       area = i_new(struct memarea, 1);
+       area->data = data;
+       area->size = size;
+       area->callback = callback;
+       area->context = context;
+       area->refcount = 1;
+       return area;
+}
+
+struct memarea *memarea_init_empty(void)
+{
+       i_assert(memarea_empty.refcount > 0);
+       memarea_empty.refcount++;
+       return &memarea_empty;
+}
+
+void memarea_ref(struct memarea *area)
+{
+       i_assert(area->refcount > 0);
+       area->refcount++;
+}
+
+void memarea_unref(struct memarea **_area)
+{
+       struct memarea *area = *_area;
+
+       *_area = NULL;
+       i_assert(area->refcount > 0);
+
+       if (--area->refcount > 0)
+               return;
+       i_assert(area != &memarea_empty);
+       area->callback(area->context);
+       i_free(area);
+}
+
+void memarea_free_without_callback(struct memarea **_area)
+{
+       struct memarea *area = *_area;
+
+       *_area = NULL;
+       i_assert(memarea_get_refcount(area) == 1);
+       i_free(area);
+}
+
+unsigned int memarea_get_refcount(struct memarea *area)
+{
+       i_assert(area->refcount > 0);
+       return area->refcount;
+}
+
+const void *memarea_get(struct memarea *area, size_t *size_r)
+{
+       *size_r = area->size;
+       return area->data;
+}
+
+size_t memarea_get_size(struct memarea *area)
+{
+       return area->size;
+}
+
+void memarea_free_callback_noop(void *context ATTR_UNUSED)
+{
+}
diff --git a/src/lib/memarea.h b/src/lib/memarea.h
new file mode 100644 (file)
index 0000000..a01659f
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef MEMAREA_H
+#define MEMAREA_H
+
+typedef void memarea_free_callback_t(void *context);
+
+/* Create reference counted memory area. The callback is called when the
+   refcount drops to 0. */
+struct memarea *
+memarea_init(const void *data, size_t size,
+            memarea_free_callback_t *callback, void *context);
+#define memarea_init(data, size, callback, context) \
+       memarea_init(data, size + \
+               CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \
+               (memarea_free_callback_t *)callback, context)
+/* Returns an empty memory area. */
+struct memarea *memarea_init_empty(void);
+
+void memarea_ref(struct memarea *area);
+void memarea_unref(struct memarea **area);
+/* Free the memory area without calling the callback.
+   This is allowed only when refcount==1. */
+void memarea_free_without_callback(struct memarea **area);
+
+unsigned int memarea_get_refcount(struct memarea *area);
+const void *memarea_get(struct memarea *area, size_t *size_r);
+size_t memarea_get_size(struct memarea *area);
+
+/* free-callback that does nothing */
+void memarea_free_callback_noop(void *context);
+
+#endif
index 932c2dc838e4423b4746eb9126bb49f810557627..255c5ab66cf7a04a94aadddd565076f10f86223f 100644 (file)
@@ -49,6 +49,7 @@ TEST(test_llist)
 TEST(test_log_throttle)
 TEST(test_malloc_overflow)
 FATAL(fatal_malloc_overflow)
+TEST(test_memarea)
 TEST(test_mempool)
 FATAL(fatal_mempool)
 TEST(test_mempool_alloconly)
diff --git a/src/lib/test-memarea.c b/src/lib/test-memarea.c
new file mode 100644 (file)
index 0000000..406d575
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "buffer.h"
+#include "memarea.h"
+
+static bool test_callback_called = FALSE;
+
+static void test_callback(buffer_t *buf)
+{
+       test_assert(!test_callback_called);
+       test_callback_called = TRUE;
+       buffer_free(&buf);
+}
+
+void test_memarea(void)
+{
+       struct memarea *area, *area2;
+       buffer_t *buf;
+       size_t size;
+
+       test_begin("memarea");
+       buf = buffer_create_dynamic(default_pool, 128);
+       buffer_append(buf, "123", 3);
+
+       area = memarea_init(buf->data, buf->used, test_callback, buf);
+       test_assert(memarea_get_refcount(area) == 1);
+       test_assert(memarea_get(area, &size) == buf->data && size == buf->used);
+
+       area2 = area;
+       memarea_ref(area2);
+       test_assert(memarea_get_refcount(area2) == 2);
+       test_assert(memarea_get(area2, &size) == buf->data && size == buf->used);
+       memarea_unref(&area2);
+       test_assert(area2 == NULL);
+       test_assert(!test_callback_called);
+
+       memarea_unref(&area);
+       test_assert(test_callback_called);
+
+       test_end();
+}