From: Timo Sirainen Date: Fri, 8 Sep 2017 10:33:27 +0000 (+0300) Subject: lib: Add memarea API X-Git-Tag: 2.3.0.rc1~652 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4c7d704408fc77467143a945dc2d0a856f72e674;p=thirdparty%2Fdovecot%2Fcore.git lib: Add memarea API This can be used to create reference counted memory areas where a callback is called once the refcount drops to zero. --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 949ce18480..b6d2231682 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -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 index 0000000000..f4bf0170c2 --- /dev/null +++ b/src/lib/memarea.c @@ -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 index 0000000000..a01659f1ce --- /dev/null +++ b/src/lib/memarea.h @@ -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 diff --git a/src/lib/test-lib.inc b/src/lib/test-lib.inc index 932c2dc838..255c5ab66c 100644 --- a/src/lib/test-lib.inc +++ b/src/lib/test-lib.inc @@ -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 index 0000000000..406d575544 --- /dev/null +++ b/src/lib/test-memarea.c @@ -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(); +}