context UPDATE use hash table instead of pkey for errors
There was a limitaion of max 1024 (Linux) pthread
keys per process so it was not possible to create more.
Fixes #2000
diff --git a/src/common.h b/src/common.h
index ad7a040..bb4e49d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -311,6 +311,14 @@
*****************************************************************************/
/**
+ * @brief Context error hash table record.
+ */
+struct ly_ctx_err_rec {
+ struct ly_err_item *err; /** pointer to the error items, if any */
+ pthread_t tid; /** pthread thread ID */
+};
+
+/**
* @brief Context of the YANG schemas
*/
struct ly_ctx {
@@ -327,7 +335,7 @@
ly_ext_data_clb ext_clb; /**< optional callback for providing extension-specific run-time data for extensions */
void *ext_clb_data; /**< optional private data for ::ly_ctx.ext_clb */
- pthread_key_t errlist_key; /**< key for the thread-specific list of errors related to the context */
+ struct ly_ht *err_ht; /**< hash table of thread-specific list of errors related to the context */
pthread_mutex_t lyb_hash_lock; /**< lock for storing LYB schema hashes in schema nodes */
};
diff --git a/src/context.c b/src/context.c
index 54c2fc5..c2c5bab 100644
--- a/src/context.c
+++ b/src/context.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Context implementations
*
- * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -228,6 +228,17 @@
return mod;
}
+/**
+ * @brief Hash table value-equal callback for comparing context error hash table record.
+ */
+static ly_bool
+ly_ctx_ht_err_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct ly_ctx_err_rec *err1 = val1_p, *err2 = val2_p;
+
+ return err1->tid == err2->tid;
+}
+
LIBYANG_API_DEF LY_ERR
ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
{
@@ -251,8 +262,9 @@
/* plugins */
LY_CHECK_ERR_GOTO(lyplg_init(), LOGINT(NULL); rc = LY_EINT, cleanup);
- /* initialize thread-specific keys */
- while ((pthread_key_create(&ctx->errlist_key, ly_err_free)) == EAGAIN) {}
+ /* initialize thread-specific error hash table */
+ ctx->err_ht = lyht_new(1, sizeof(struct ly_ctx_err_rec), ly_ctx_ht_err_equal_cb, NULL, 1);
+ LY_CHECK_ERR_GOTO(!ctx->err_ht, rc = LY_EMEM, cleanup);
/* init LYB hash lock */
pthread_mutex_init(&ctx->lyb_hash_lock, NULL);
@@ -1247,6 +1259,19 @@
return ret;
}
+/**
+ * @brief Callback for freeing context error hash table values.
+ *
+ * @param[in] val_p Pointer to a pointer to an error item to free with all the siblings.
+ */
+static void
+ly_ctx_ht_err_rec_free(void *val_p)
+{
+ struct ly_ctx_err_rec *err = val_p;
+
+ ly_err_free(err->err);
+}
+
LIBYANG_API_DEF void
ly_ctx_destroy(struct ly_ctx *ctx)
{
@@ -1279,9 +1304,8 @@
/* leftover unres */
lys_unres_glob_erase(&ctx->unres);
- /* clean the error list */
- ly_err_clean(ctx, 0);
- pthread_key_delete(ctx->errlist_key);
+ /* clean the error hash table */
+ lyht_free(ctx->err_ht, ly_ctx_ht_err_rec_free);
/* dictionary */
lydict_clean(&ctx->dict);
diff --git a/src/hash_table.h b/src/hash_table.h
index bec7c86..170f5b9 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -167,7 +167,7 @@
* @brief Free a hash table.
*
* @param[in] ht Hash table to be freed.
- * @param[in] val_free Optional callback for freeing allthe stored values, @p val_p is a pointer to a stored value.
+ * @param[in] val_free Optional callback for freeing all the stored values, @p val_p is a pointer to a stored value.
*/
void lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p));
diff --git a/src/log.c b/src/log.c
index 74407d8..691741b 100644
--- a/src/log.c
+++ b/src/log.c
@@ -173,77 +173,112 @@
return e->no;
}
+/**
+ * @brief Get error record from error hash table of a context for the current thread.
+ *
+ * @param[in] ctx Context to use.
+ * @return Thread error record, if any.
+ */
+static struct ly_ctx_err_rec *
+ly_err_get_rec(const struct ly_ctx *ctx)
+{
+ struct ly_ctx_err_rec rec, *match = NULL;
+
+ /* prepare record */
+ rec.tid = pthread_self();
+
+ /* get the pointer to the matching record */
+ lyht_find(ctx->err_ht, &rec, dict_hash((void *)&rec.tid, sizeof rec.tid), (void **)&match);
+
+ return match;
+}
+
LIBYANG_API_DEF struct ly_err_item *
ly_err_first(const struct ly_ctx *ctx)
{
+ struct ly_ctx_err_rec *rec;
+
LY_CHECK_ARG_RET(NULL, ctx, NULL);
- return pthread_getspecific(ctx->errlist_key);
+ /* get the pointer to the matching record */
+ rec = ly_err_get_rec(ctx);
+
+ return rec ? rec->err : NULL;
}
LIBYANG_API_DEF struct ly_err_item *
ly_err_last(const struct ly_ctx *ctx)
{
- const struct ly_err_item *e;
+ struct ly_ctx_err_rec *rec;
LY_CHECK_ARG_RET(NULL, ctx, NULL);
- e = pthread_getspecific(ctx->errlist_key);
- return e ? e->prev : NULL;
+ /* get the pointer to the matching record */
+ if (!(rec = ly_err_get_rec(ctx))) {
+ return NULL;
+ }
+
+ return rec->err ? rec->err->prev : NULL;
}
void
ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx)
{
- const struct ly_err_item *e;
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *err = NULL;
- /* clear any current errors */
- ly_err_clean(trg_ctx, NULL);
-
- /* get the errors in src */
- e = pthread_getspecific(src_ctx->errlist_key);
- pthread_setspecific(src_ctx->errlist_key, NULL);
+ /* get and remove the errors from src */
+ rec = ly_err_get_rec(src_ctx);
+ if (rec) {
+ err = rec->err;
+ rec->err = NULL;
+ }
/* set them for trg */
- pthread_setspecific(trg_ctx->errlist_key, e);
+ rec = ly_err_get_rec(trg_ctx);
+ ly_err_free(rec->err);
+ rec->err = err;
}
LIBYANG_API_DEF void
ly_err_free(void *ptr)
{
- struct ly_err_item *i, *next;
+ struct ly_err_item *e, *next;
/* clean the error list */
- for (i = (struct ly_err_item *)ptr; i; i = next) {
- next = i->next;
- free(i->msg);
- free(i->path);
- free(i->apptag);
- free(i);
+ LY_LIST_FOR_SAFE(ptr, next, e) {
+ free(e->msg);
+ free(e->path);
+ free(e->apptag);
+ free(e);
}
}
LIBYANG_API_DEF void
ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
{
- struct ly_err_item *i, *first;
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *e;
- first = ly_err_first(ctx);
- if (first == eitem) {
+ if (!(rec = ly_err_get_rec(ctx))) {
+ return;
+ }
+ if (rec->err == eitem) {
eitem = NULL;
}
- if (eitem) {
+
+ if (!eitem) {
+ /* free all err */
+ ly_err_free(rec->err);
+ rec->err = NULL;
+ } else {
/* disconnect the error */
- for (i = first; i && (i->next != eitem); i = i->next) {}
- assert(i);
- i->next = NULL;
- first->prev = i;
+ for (e = rec->err; e && (e->next != eitem); e = e->next) {}
+ assert(e);
+ e->next = NULL;
+ rec->err->prev = e;
/* free this err and newer */
ly_err_free(eitem);
- } else {
- /* free all err */
- ly_err_free(first);
- pthread_setspecific(ctx->errlist_key, NULL);
}
}
@@ -356,64 +391,89 @@
}
}
+/**
+ * @brief Store generated error in a context.
+ *
+ * @param[in] ctx Context to use.
+ * @param[in] level Message log level.
+ * @param[in] no Error number.
+ * @param[in] vecode Error validation error code.
+ * @param[in] msg Error message, always spent.
+ * @param[in] path Error path, always spent.
+ * @param[in] apptag Error app tag, always spent.
+ * @return LY_ERR value.
+ */
static LY_ERR
log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
{
- struct ly_err_item *eitem, *last;
+ struct ly_ctx_err_rec *rec, new;
+ struct ly_err_item *e, *last;
+ LY_ERR r;
assert(ctx && (level < LY_LLVRB));
- eitem = pthread_getspecific(ctx->errlist_key);
- if (!eitem) {
+ if (!(rec = ly_err_get_rec(ctx))) {
+ /* insert a new record */
+ new.err = NULL;
+ new.tid = pthread_self();
+ r = lyht_insert(ctx->err_ht, &new, dict_hash((void *)&new.tid, sizeof new.tid), (void **)&rec);
+ if (r) {
+ /* should never happen */
+ return r;
+ }
+ }
+
+ e = rec->err;
+ if (!e) {
/* if we are only to fill in path, there must have been an error stored */
assert(msg);
- eitem = malloc(sizeof *eitem);
- LY_CHECK_GOTO(!eitem, mem_fail);
- eitem->prev = eitem;
- eitem->next = NULL;
+ e = malloc(sizeof *e);
+ LY_CHECK_GOTO(!e, mem_fail);
+ e->prev = e;
+ e->next = NULL;
- pthread_setspecific(ctx->errlist_key, eitem);
+ rec->err = e;
} else if (!msg) {
/* only filling the path */
assert(path);
/* find last error */
- eitem = eitem->prev;
+ e = e->prev;
do {
- if (eitem->level == LY_LLERR) {
+ if (e->level == LY_LLERR) {
/* fill the path */
- free(eitem->path);
- eitem->path = path;
+ free(e->path);
+ e->path = path;
return LY_SUCCESS;
}
- eitem = eitem->prev;
- } while (eitem->prev->next);
+ e = e->prev;
+ } while (e->prev->next);
/* last error was not found */
assert(0);
} else if ((temp_ly_log_opts && ((*temp_ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) ||
(!temp_ly_log_opts && ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST))) {
/* overwrite last message */
- free(eitem->msg);
- free(eitem->path);
- free(eitem->apptag);
+ free(e->msg);
+ free(e->path);
+ free(e->apptag);
} else {
/* store new message */
- last = eitem->prev;
- eitem->prev = malloc(sizeof *eitem);
- LY_CHECK_GOTO(!eitem->prev, mem_fail);
- eitem = eitem->prev;
- eitem->prev = last;
- eitem->next = NULL;
- last->next = eitem;
+ last = e->prev;
+ e->prev = malloc(sizeof *e);
+ LY_CHECK_GOTO(!e->prev, mem_fail);
+ e = e->prev;
+ e->prev = last;
+ e->next = NULL;
+ last->next = e;
}
/* fill in the information */
- eitem->level = level;
- eitem->no = no;
- eitem->vecode = vecode;
- eitem->msg = msg;
- eitem->path = path;
- eitem->apptag = apptag;
+ e->level = level;
+ e->no = no;
+ e->vecode = vecode;
+ e->msg = msg;
+ e->path = path;
+ e->apptag = apptag;
return LY_SUCCESS;
mem_fail: