]> uap-core.de Git - note.git/commitdiff
update ucx and use temp mempool/cxMempoolTransfer for thread safety
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 11 Apr 2025 19:36:40 +0000 (21:36 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 11 Apr 2025 19:36:40 +0000 (21:36 +0200)
14 files changed:
application/store.c
ucx/buffer.c
ucx/cx/array_list.h
ucx/cx/buffer.h
ucx/cx/common.h
ucx/cx/linked_list.h
ucx/cx/list.h
ucx/cx/mempool.h
ucx/cx/printf.h
ucx/cx/properties.h
ucx/cx/string.h
ucx/linked_list.c
ucx/mempool.c
ucx/printf.c

index 1af0fc7edd304da13612d8536fe31e7d5c5d8a7c..75be1a7060f7860f1e3bc0d9ed07fb13616038a6 100644 (file)
@@ -703,14 +703,13 @@ typedef struct LoadAttachmentsJob {
 
 
 static int qthr_load_attachments(LoadAttachmentsJob *job) {
-    //CxMempool *mp = cxMempoolCreateSimple(32);
-    //job->temp_mp = mp;
-    // TODO: use separate mempool and transfer it to the resource mempool
+    CxMempool *mp = cxMempoolCreateSimple(32);
+    job->temp_mp = mp;
     
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_ATTACHMENTS_GET);
     dbuQuerySetParamInt64(q, 1, job->note->resource_id);
-    DBUObjectBuilder *builder = dbuObjectBuilder(attachments_class, q, job->note->model->note_allocator); // use mp mempool allocator
+    DBUObjectBuilder *builder = dbuObjectBuilder(attachments_class, q, mp->allocator);
     job->result =  dbuObjectBuilderGetList(builder);
     if(!job->result) {
         job->error = 1;
@@ -722,7 +721,10 @@ static int qthr_load_attachments(LoadAttachmentsJob *job) {
 
 static void uithr_load_attachments_finished(UiEvent *event, LoadAttachmentsJob *job) {
     if(job->result) {
-        job->note->attachments = job->result; // TODO: transfer temp_mp first
+        CxMempool *note_mp = job->note->model->note_allocator->data;
+        cxMempoolTransfer(job->temp_mp, note_mp);
+        cxMempoolFree(job->temp_mp);
+        job->note->attachments = job->result;
     }
     
     if(job->resultcb) {
index 1a7713eec5b298f4bed8ef1ab69c54cf5234720a..981b161069a1783d0284bc688b1802bf56338a75 100644 (file)
@@ -139,6 +139,7 @@ int cxBufferSeek(
             npos = 0;
             break;
         default:
+            errno = EINVAL;
             return -1;
     }
 
@@ -399,10 +400,8 @@ int cxBufferPut(
 }
 
 int cxBufferTerminate(CxBuffer *buffer) {
-    bool success = 0 == cxBufferPut(buffer, 0);
-    if (success) {
-        buffer->pos--;
-        buffer->size--;
+    if (0 == cxBufferPut(buffer, 0)) {
+        buffer->size = buffer->pos - 1;
         return 0;
     } else {
         return -1;
index 198407f85ffe750a3e5d462bcedc9e7941b45902..847540a5cd830b787cf01683f316d1af51da9b88 100644 (file)
@@ -727,7 +727,7 @@ CxList *cxArrayListCreate(
  *
  * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
  * copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * to cx_cmp_ptr().
  *
  * @param elem_size (@c size_t) the size of each element in bytes
  * @param initial_capacity (@c size_t) the initial number of elements the array can store
index e2868326bbb0cbe4dec92fd824706d92fe10e9f3..43305803338331518013dda7bce5c77302be9759 100644 (file)
@@ -674,11 +674,10 @@ int cxBufferPut(
 /**
  * Writes a terminating zero to a buffer at the current position.
  *
- * On successful write, @em neither the position @em nor the size of the buffer is
- * increased.
+ * If successful, sets the size to the current position and advances the position by one.
  *
  * The purpose of this function is to have the written data ready to be used as
- * a C string.
+ * a C string with the buffer's size being the length of that string.
  *
  * @param buffer the buffer to write to
  * @return zero, if the terminator could be written, non-zero otherwise
index 604ca5f617c1ac68eff676929468986806ce7b16..b54153d636145117d1b3a8589a642ebcb27dd1af 100644 (file)
@@ -46,7 +46,7 @@
  * Repositories:<br>
  * <a href="https://sourceforge.net/p/ucx/code">https://sourceforge.net/p/ucx/code</a>
  * -&nbsp;or&nbsp;-
- * <a href="https://develop.uap-core.de/hg/ucx">https://develop.uap-core.de/hg/ucx</a>
+ * <a href="https://uap-core.de/hg/ucx">https://uap-core.de/hg/ucx</a>
  * </p>
  *
  * <h2>LICENCE</h2>
  */
 #define cx_attr_malloc __attribute__((__malloc__))
 
-#ifndef __clang__
+#if !defined(__clang__) && __GNUC__ >= 11
 /**
  * The pointer returned by the attributed function is supposed to be freed
  * by @p freefunc.
index fcdae7490e69860c1b4597b529b9046ed602257b..94185d60c32cf2bfa38a01d7fff2cf97a04f397c 100644 (file)
@@ -77,7 +77,7 @@ CxList *cxLinkedListCreate(
  *
  * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
  * copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * to cx_cmp_ptr().
  *
  * @param elem_size (@c size_t) the size of each element in bytes
  * @return (@c CxList*) the created list
@@ -393,7 +393,7 @@ void cx_linked_list_insert_sorted_chain(
  * @li @p loc_next and @p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance)
  * @li @p loc_next and @p begin (ancestor node is determined by list traversal, overall O(n) performance)
  *
- * @remark The @c next and @c prev pointers of the removed node are not cleared by this function and may still be used
+ * @remark The @c next and @c prev pointers of the removed chain are not cleared by this function and may still be used
  * to traverse to a former adjacent node in the list, or within the chain.
  *
  * @param begin a pointer to the beginning node pointer (optional)
index 2df6c506002096f10066de8672c353e93f912a71..a358d02304a2a850c1959e85998885f7d45b8241 100644 (file)
@@ -894,6 +894,7 @@ static inline size_t cxListFindRemove(
  */
 cx_attr_nonnull
 static inline void cxListSort(CxList *list) {
+    if (list->collection.sorted) return;
     list->cl->sort(list);
     list->collection.sorted = true;
 }
index 43b3b173a20c24d57e0d0184e811e9b387de94fd..35bf34e53e91ba46e3a5d0bc51b2bbee72e86967 100644 (file)
@@ -156,6 +156,49 @@ int cxMempoolRegister(
         cx_destructor_func destr
 );
 
+/**
+ * Transfers all the memory managed by one pool to another.
+ *
+ * The allocator of the source pool will also be transferred and registered with the destination pool
+ * and stays valid, as long as the destination pool is not destroyed.
+ *
+ * The source pool will get a completely new allocator and can be reused or destroyed afterward.
+ *
+ * @param source the pool to move the memory from
+ * @param dest the pool where to transfer the memory to
+ * @retval zero success
+ * @retval non-zero failure
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolTransfer(
+        CxMempool *source,
+        CxMempool *dest
+);
+
+/**
+ * Transfers an object from one pool to another.
+ *
+ * You can only transfer objects that have been allocated by this pool.
+ * Objects that have been registered with cxMempoolRegister() cannot be transferred this way.
+ *
+ * @attention If the object maintains a reference to the pool's allocator,
+ * you must make sure to update that reference to the allocator of the destination pool.
+ *
+ * @param source the pool to move the memory from
+ * @param dest the pool where to transfer the memory to
+ * @param obj pointer to the object that shall be transferred
+ * @retval zero success
+ * @retval non-zero failure or object was not found in the source pool
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolTransferObject(
+        CxMempool *source,
+        CxMempool *dest,
+        const void *obj
+);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index f02a80fb9457d12b196c9b9175fe5c3f89c635a7..4ffd05f3381887dadaa76b78aa41e16b5b2b9baa 100644 (file)
@@ -229,7 +229,7 @@ cx_attr_printf(4, 5)
 cx_attr_cstr_arg(4)
 cx_attr_export
 int cx_sprintf_a(
-        CxAllocator *alloc,
+        const CxAllocator *alloc,
         char **str,
         size_t *len,
         const char *fmt,
@@ -274,7 +274,7 @@ cx_attr_access_rw(2)
 cx_attr_access_rw(3)
 cx_attr_export
 int cx_vsprintf_a(
-        CxAllocator *alloc,
+        const CxAllocator *alloc,
         char **str,
         size_t *len,
         const char *fmt,
@@ -333,7 +333,7 @@ cx_attr_access_rw(3)
 cx_attr_access_rw(4)
 cx_attr_export
 int cx_sprintf_sa(
-        CxAllocator *alloc,
+        const CxAllocator *alloc,
         char *buf,
         size_t *len,
         char **str,
@@ -388,7 +388,7 @@ cx_attr_nonnull
 cx_attr_cstr_arg(5)
 cx_attr_export
 int cx_vsprintf_sa(
-        CxAllocator *alloc,
+        const CxAllocator *alloc,
         char *buf,
         size_t *len,
         char **str,
index 619229f906e37df086022dfe47be8f88e3e2d0ff..2942738a59d25a4dcea77f7d97acbbce5557d147 100644 (file)
@@ -551,10 +551,12 @@ CxPropertiesStatus cxPropertiesNext(
 /**
  * Creates a properties sink for an UCX map.
  *
- * The values stored in the map will be pointers to strings allocated
- * by #cx_strdup_a().
+ * The values stored in the map will be pointers to freshly allocated,
+ * zero-terminated C strings (@c char*), which means the @p map should have been
+ * created with #CX_STORE_POINTERS.
+ *
  * The default stdlib allocator will be used, unless you specify a custom
- * allocator in the optional @c data of the sink.
+ * allocator in the optional @c data field of the returned sink.
  *
  * @param map the map that shall consume the k/v-pairs.
  * @return the sink
index 64c8dbaa0260f6f3f9756a8a079441c6df9a93c2..fee35d2acb4b424bf0b0b2a8e950c97f2107fd38 100644 (file)
@@ -151,7 +151,7 @@ extern "C" {
  *
  * @param literal the string literal
  */
-#define CX_STR(literal) (cxstring){literal, sizeof(literal) - 1}
+#define CX_STR(literal) ((cxstring){literal, sizeof(literal) - 1})
 
 #endif
 
@@ -879,7 +879,7 @@ cxmutstr cx_strdup_a_(
  * @see cx_strfree_a()
  */
 #define cx_strdup_a(allocator, string) \
-    cx_strdup_a_((allocator), cx_strcast((string)))
+    cx_strdup_a_((allocator), cx_strcast(string))
 
 /**
  * Creates a duplicate of the specified string.
@@ -894,7 +894,7 @@ cxmutstr cx_strdup_a_(
  * @see cx_strdup_a()
  * @see cx_strfree()
  */
-#define cx_strdup(string) cx_strdup_a_(cxDefaultAllocator, string)
+#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
 
 /**
  * Omits leading and trailing spaces.
@@ -1091,7 +1091,7 @@ CxStrtokCtx cx_strtok_(
  * @return (@c CxStrtokCtx) a new string tokenization context
  */
 #define cx_strtok(str, delim, limit) \
-    cx_strtok_(cx_strcast((str)), cx_strcast((delim)), (limit))
+    cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit))
 
 /**
  * Returns the next token.
index 3833d4426f0cac7393ceeb78e6c2d7b6088ac364..66cf543c5382c0af3904aad1de1078d6e553003b 100644 (file)
@@ -920,6 +920,8 @@ static size_t cx_ll_find_remove(
         const void *elem,
         bool remove
 ) {
+    if (list->collection.size == 0) return 0;
+
     size_t index;
     cx_linked_list *ll = ((cx_linked_list *) list);
     cx_linked_list_node *node = cx_linked_list_find(
index fea4d7026dcdd3283c0c2de5935544aefc2f7db7..1080c552842a2d30bc1c051515fb404434825435 100644 (file)
@@ -38,24 +38,34 @@ struct cx_mempool_memory_s {
     char c[];
 };
 
+static int cx_mempool_ensure_capacity(
+        struct cx_mempool_s *pool,
+        size_t needed_capacity
+) {
+    if (needed_capacity <= pool->capacity) return 0;
+    size_t newcap = pool->capacity >= 1000 ?
+        pool->capacity + 1000 : pool->capacity * 2;
+    size_t newmsize;
+    if (pool->capacity > newcap || cx_szmul(newcap,
+            sizeof(struct cx_mempool_memory_s*), &newmsize)) {
+        errno = EOVERFLOW;
+        return 1;
+    }
+    struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize);
+    if (newdata == NULL) return 1;
+    pool->data = newdata;
+    pool->capacity = newcap;
+    return 0;
+}
+
 static void *cx_mempool_malloc(
         void *p,
         size_t n
 ) {
     struct cx_mempool_s *pool = p;
 
-    if (pool->size >= pool->capacity) {
-        size_t newcap = pool->capacity - (pool->capacity % 16) + 16;
-        size_t newmsize;
-        if (pool->capacity > newcap || cx_szmul(newcap,
-                sizeof(struct cx_mempool_memory_s*), &newmsize)) {
-            errno = EOVERFLOW;
-            return NULL;
-        }
-        struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize);
-        if (newdata == NULL) return NULL;
-        pool->data = newdata;
-        pool->capacity = newcap;
+    if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+        return NULL;
     }
 
     struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n);
@@ -235,3 +245,73 @@ CxMempool *cxMempoolCreate(
 
     return pool;
 }
+
+int cxMempoolTransfer(
+        CxMempool *source,
+        CxMempool *dest
+) {
+    // safety check
+    if (source == dest) return 1;
+
+    // ensure enough capacity in the destination pool
+    if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) {
+        return 1; // LCOV_EXCL_LINE
+    }
+
+    // allocate a replacement allocator for the source pool
+    CxAllocator *new_source_allocator = malloc(sizeof(CxAllocator));
+    if (new_source_allocator == NULL) { // LCOV_EXCL_START
+        return 1;
+    } // LCOV_EXCL_STOP
+    new_source_allocator->cl = &cx_mempool_allocator_class;
+    new_source_allocator->data = source;
+
+    // transfer all the data
+    memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size);
+    dest->size += source->size;
+
+    // register the old allocator with the new pool
+    // we have to remove const-ness for this, but that's okay here
+    CxAllocator *transferred_allocator = (CxAllocator*) source->allocator;
+    transferred_allocator->data = dest;
+    cxMempoolRegister(dest, transferred_allocator, free);
+
+    // prepare the source pool for re-use
+    source->allocator = new_source_allocator;
+    memset(source->data, 0, source->size * sizeof(source->data[0]));
+    source->size = 0;
+
+    return 0;
+}
+
+int cxMempoolTransferObject(
+        CxMempool *source,
+        CxMempool *dest,
+        const void *obj
+) {
+    // safety check
+    if (source == dest) return 1;
+
+    // first, make sure that the dest pool can take the object
+    if (cx_mempool_ensure_capacity(dest, dest->size + 1)) {
+        return 1; // LCOV_EXCL_LINE
+    }
+    // search for the object
+    for (size_t i = 0; i < source->size; i++) {
+        struct cx_mempool_memory_s *mem = source->data[i];
+        if (mem->c == obj) {
+            // remove from the source pool
+            size_t last_index = source->size - 1;
+            if (i != last_index) {
+                source->data[i] = source->data[last_index];
+                source->data[last_index] = NULL;
+            }
+            source->size--;
+            // add to the target pool
+            dest->data[dest->size++] = mem;
+            return 0;
+        }
+    }
+    // not found
+    return 1;
+}
index bf691cf63008b6d53a57c5c90cb219ee87cc834e..a699a0fcd111443f532b2b8599141d3e79ed49cc 100644 (file)
@@ -132,7 +132,13 @@ cxmutstr cx_vasprintf_a(
     return s;
 }
 
-int cx_sprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, ... ) {
+int cx_sprintf_a(
+        const CxAllocator *alloc,
+        char **str,
+        size_t *len,
+        const char *fmt,
+        ...
+) {
     va_list ap;
     va_start(ap, fmt);
     int ret = cx_vsprintf_a(alloc, str, len, fmt, ap);
@@ -140,7 +146,13 @@ int cx_sprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, .
     return ret;
 }
 
-int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap) {
+int cx_vsprintf_a(
+        const CxAllocator *alloc,
+        char **str,
+        size_t *len,
+        const char *fmt,
+        va_list ap
+) {
     va_list ap2;
     va_copy(ap2, ap);
     int ret = vsnprintf(*str, *len, fmt, ap);
@@ -162,7 +174,14 @@ int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt,
     return ret;
 }
 
-int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ... ) {
+int cx_sprintf_sa(
+        const CxAllocator *alloc,
+        char *buf,
+        size_t *len,
+        char **str,
+        const char *fmt,
+        ...
+) {
     va_list ap;
     va_start(ap, fmt);
     int ret = cx_vsprintf_sa(alloc, buf, len, str, fmt, ap);
@@ -170,7 +189,14 @@ int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const
     return ret;
 }
 
-int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap) {
+int cx_vsprintf_sa(
+        const CxAllocator *alloc,
+        char *buf,
+        size_t *len,
+        char **str,
+        const char *fmt,
+        va_list ap
+) {
     va_list ap2;
     va_copy(ap2, ap);
     int ret = vsnprintf(buf, *len, fmt, ap);