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/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: