]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-dict: dict_transaction_commit_async() - Never call callback immediately
authorAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 7 Sep 2020 07:56:34 +0000 (10:56 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Thu, 10 Sep 2020 11:47:34 +0000 (11:47 +0000)
This could cause confusion for the callers. Although so far all the callers
have handled it fine.

Use this wrappers for all dict drivers, even if they support async commits
themselves. This is because many of them were still calling the callback
immediately on error handling.

src/lib-dict/dict-private.h
src/lib-dict/dict.c

index 01d55ede1c2601cbfbaf80735cb67fa5c4d10406..6aa96e61991699e3f8509b408e1cd4c48352ce5b 100644 (file)
@@ -47,6 +47,8 @@ struct dict_vfuncs {
                              const struct timespec *ts);
 };
 
+struct dict_commit_callback_ctx;
+
 struct dict {
        const char *name;
 
@@ -57,6 +59,7 @@ struct dict {
        int refcount;
        struct event *event;
        struct ioloop *ioloop, *prev_ioloop;
+       struct dict_commit_callback_ctx *commits;
 };
 
 struct dict_iterate_context {
index 26a90cafdf41a167219b124f69a507326c0f15b3..68039bc24514bacedcd723438d5cb4267e9380ea 100644 (file)
 #include "dict-private.h"
 
 struct dict_commit_callback_ctx {
+       pool_t pool;
+       struct dict_commit_callback_ctx *prev, *next;
        struct dict *dict;
        struct event *event;
        dict_transaction_commit_callback_t *callback;
+       struct timeout *to;
        void *context;
+       struct dict_commit_result result;
+       bool background:1;
 };
 
 struct dict_lookup_callback_ctx {
@@ -25,6 +30,9 @@ struct dict_lookup_callback_ctx {
 
 static ARRAY(struct dict *) dict_drivers;
 
+static void
+dict_commit_async_timeout(struct dict_commit_callback_ctx *ctx);
+
 static struct event_category event_category_dict = {
        .name = "dict",
 };
@@ -159,6 +167,8 @@ void dict_wait(struct dict *dict)
        e_debug(dict->event, "Waiting for dict to finish pending operations");
        if (dict->v.wait != NULL)
                dict->v.wait(dict);
+       while (dict->commits != NULL)
+               dict_commit_async_timeout(dict->commits);
 }
 
 bool dict_switch_ioloop(struct dict *dict)
@@ -240,24 +250,37 @@ dict_lookup_callback(const struct dict_lookup_result *result,
        i_free(ctx);
 }
 
-static void dict_commit_callback(const struct dict_commit_result *result,
-                                void *context)
+static void
+dict_commit_async_timeout(struct dict_commit_callback_ctx *ctx)
 {
-       struct dict_commit_callback_ctx *ctx = context;
-
-       i_assert(result->ret >= 0 || result->error != NULL);
+       DLLIST_REMOVE(&ctx->dict->commits, ctx);
+       timeout_remove(&ctx->to);
        dict_pre_api_callback(ctx->dict);
        if (ctx->callback != NULL)
-               ctx->callback(result, ctx->context);
-       else if (result->ret < 0) {
-               e_error(ctx->event, "Commit failed: %s", result->error);
-       }
+               ctx->callback(&ctx->result, ctx->context);
+       else if (ctx->result.ret < 0)
+               e_error(ctx->event, "Commit failed: %s", ctx->result.error);
        dict_post_api_callback(ctx->dict);
 
-       dict_transaction_finished(ctx->event, result->ret, FALSE, result->error);
+       dict_transaction_finished(ctx->event, ctx->result.ret, FALSE, ctx->result.error);
        event_unref(&ctx->event);
        dict_unref(&ctx->dict);
-       i_free(ctx);
+       pool_unref(&ctx->pool);
+}
+
+static void dict_commit_callback(const struct dict_commit_result *result,
+                                void *context)
+{
+       struct dict_commit_callback_ctx *ctx = context;
+
+       i_assert(result->ret >= 0 || result->error != NULL);
+       ctx->result = *result;
+       if (ctx->background) {
+               ctx->result.error = p_strdup(ctx->pool, ctx->result.error);
+               ctx->to = timeout_add_short(0, dict_commit_async_timeout, ctx);
+       } else {
+               dict_commit_async_timeout(ctx);
+       }
 }
 
 int dict_lookup(struct dict *dict, pool_t pool, const char *key,
@@ -473,17 +496,19 @@ dict_transaction_commit_sync_callback(const struct dict_commit_result *result,
 int dict_transaction_commit(struct dict_transaction_context **_ctx,
                            const char **error_r)
 {
+       pool_t pool = pool_alloconly_create("dict_commit_callback_ctx", 64);
        struct dict_commit_callback_ctx *cctx =
-               i_new(struct dict_commit_callback_ctx, 1);
+               p_new(pool, struct dict_commit_callback_ctx, 1);
        struct dict_transaction_context *ctx = *_ctx;
        struct dict_commit_sync_result result;
 
        *_ctx = NULL;
-
+       cctx->pool = pool;
        i_zero(&result);
        i_assert(ctx->dict->transaction_count > 0);
        ctx->dict->transaction_count--;
        DLLIST_REMOVE(&ctx->dict->transactions, ctx);
+       DLLIST_PREPEND(&ctx->dict->commits, cctx);
        cctx->dict = ctx->dict;
        dict_ref(cctx->dict);
        cctx->callback = dict_transaction_commit_sync_callback;
@@ -500,22 +525,25 @@ void dict_transaction_commit_async(struct dict_transaction_context **_ctx,
                                   dict_transaction_commit_callback_t *callback,
                                   void *context)
 {
+       pool_t pool = pool_alloconly_create("dict_commit_callback_ctx", 64);
        struct dict_commit_callback_ctx *cctx =
-               i_new(struct dict_commit_callback_ctx, 1);
+               p_new(pool, struct dict_commit_callback_ctx, 1);
        struct dict_transaction_context *ctx = *_ctx;
 
        *_ctx = NULL;
        i_assert(ctx->dict->transaction_count > 0);
        ctx->dict->transaction_count--;
        DLLIST_REMOVE(&ctx->dict->transactions, ctx);
+       DLLIST_PREPEND(&ctx->dict->commits, cctx);
        if (callback == NULL)
                callback = dict_transaction_commit_async_noop_callback;
+       cctx->pool = pool;
        cctx->dict = ctx->dict;
        dict_ref(cctx->dict);
        cctx->callback = callback;
        cctx->context = context;
        cctx->event = ctx->event;
-
+       cctx->background = TRUE;
        ctx->dict->v.transaction_commit(ctx, TRUE, dict_commit_callback, cctx);
 }