diff options
Diffstat (limited to 'src/dict/dict-init-cache.c')
-rw-r--r-- | src/dict/dict-init-cache.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/src/dict/dict-init-cache.c b/src/dict/dict-init-cache.c new file mode 100644 index 0000000..ed76940 --- /dev/null +++ b/src/dict/dict-init-cache.c @@ -0,0 +1,164 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "dict.h" +#include "dict-private.h" +#include "dict-init-cache.h" +#include "llist.h" + +/* How many seconds to keep dict opened for reuse after it's been closed */ +#define DICT_CACHE_TIMEOUT_SECS 30 +/* How many closed dicts to keep */ +#define DICT_CACHE_MAX_COUNT 10 + +struct dict_init_cache_list { + struct dict_init_cache_list *prev, *next; + + struct dict *dict; + char *dict_name; + int refcount; + + time_t destroy_time; +}; + +static struct dict_init_cache_list *dicts = NULL; +static struct timeout *to_dict = NULL; + +static struct dict_init_cache_list * +dict_init_cache_add(const char *dict_name, struct dict *dict) +{ + struct dict_init_cache_list *list; + + list = i_new(struct dict_init_cache_list, 1); + list->refcount = 1; + list->dict = dict; + list->dict_name = i_strdup(dict_name); + + DLLIST_PREPEND(&dicts, list); + + return list; +} + +static void dict_init_cache_list_free(struct dict_init_cache_list *list) +{ + i_assert(list->refcount == 0); + + DLLIST_REMOVE(&dicts, list); + dict_deinit(&list->dict); + i_free(list->dict_name); + i_free(list); +} + +static struct dict_init_cache_list *dict_init_cache_find(const char *dict_name) +{ + struct dict_init_cache_list *listp = dicts, *next = NULL, *match = NULL; + unsigned int ref0_count = 0; + + while (listp != NULL) { + next = listp->next; + if (match != NULL) { + /* already found the dict. we're just going through + the rest of them to drop 0 refcounts */ + } else if (strcmp(dict_name, listp->dict_name) == 0) + match = listp; + + if (listp->refcount == 0 && listp != match) { + if (listp->destroy_time <= ioloop_time || + ref0_count >= DICT_CACHE_MAX_COUNT - 1) + dict_init_cache_list_free(listp); + else + ref0_count++; + } + listp = next; + } + return match; +} + +int dict_init_cache_get(const char *dict_name, const char *uri, + const struct dict_settings *set, + struct dict **dict_r, const char **error_r) +{ + struct dict_init_cache_list *match; + int ret = 0; + + match = dict_init_cache_find(dict_name); + if (match == NULL) { + if (dict_init(uri, set, dict_r, error_r) < 0) + return -1; + match = dict_init_cache_add(dict_name, *dict_r); + } else { + match->refcount++; + *dict_r = match->dict; + } + i_assert(match->dict != NULL); + return ret; +} + +static void destroy_unrefed(void) +{ + struct dict_init_cache_list *listp, *next = NULL; + bool seen_ref0 = FALSE; + + for (listp = dicts; listp != NULL; listp = next) { + next = listp->next; + + i_assert(listp->refcount >= 0); + if (listp->refcount > 0) + ; + else if (listp->destroy_time <= ioloop_time) + dict_init_cache_list_free(listp); + else + seen_ref0 = TRUE; + } + + if (!seen_ref0 && to_dict != NULL) + timeout_remove(&to_dict); +} + +static void dict_removal_timeout(void *context ATTR_UNUSED) +{ + destroy_unrefed(); +} + +void dict_init_cache_unref(struct dict **_dict) +{ + struct dict *dict = *_dict; + struct dict_init_cache_list *listp; + + if (dict == NULL) + return; + + *_dict = NULL; + for (listp = dicts; listp != NULL; listp = listp->next) { + if (listp->dict == dict) + break; + } + + i_assert(listp != NULL && listp->dict == dict); + i_assert(listp->refcount > 0); + + listp->refcount--; + listp->destroy_time = ioloop_time + DICT_CACHE_TIMEOUT_SECS; + + if (to_dict == NULL) { + to_dict = timeout_add_to(io_loop_get_root(), + DICT_CACHE_TIMEOUT_SECS*1000/2, + dict_removal_timeout, NULL); + } +} + +void dict_init_cache_wait_all(void) +{ + struct dict_init_cache_list *listp; + + for (listp = dicts; listp != NULL; listp = listp->next) + dict_wait(listp->dict); +} + +void dict_init_cache_destroy_all(void) +{ + timeout_remove(&to_dict); + while (dicts != NULL) + dict_init_cache_list_free(dicts); +} |