From 0be689bb481fecdede6c407fc1c5cd9e97e2b167 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Fri, 10 Apr 2020 20:09:34 +0200 Subject: [PATCH] alloc: properly allocate memory handler per thread Right now the memory handler is allocated once, for the first thread that happens to call it. If pthread_getspecific is then called from another thread, the memory handler is not allocated (because pthread_once is only called for *all* threads, not per thread) and jq aborts because it thinks memory allocation failed. What we need to do instead is call pthread_key_create from the initializer, and then allocate a memory handler for every thread when it sees there's none yet. I also removed the atexit stuff, and instead use the destructor function you can pass into pthread_key_create. --- src/jv_alloc.c | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/jv_alloc.c b/src/jv_alloc.c index 5fa6768482..b1c4f0d245 100644 --- a/src/jv_alloc.c +++ b/src/jv_alloc.c @@ -48,50 +48,40 @@ static void memory_exhausted() { pthread_key_t nomem_handler_key; pthread_once_t mem_once = PTHREAD_ONCE_INIT; -static void tsd_fini(void) { - struct nomem_handler *nomem_handler; - nomem_handler = pthread_getspecific(nomem_handler_key); - if (nomem_handler) { - (void) pthread_setspecific(nomem_handler_key, NULL); - free(nomem_handler); - } +static void tsd_fini(void *data) { + pthread_setspecific(nomem_handler_key, NULL); + free(data); } static void tsd_init(void) { - if (pthread_key_create(&nomem_handler_key, NULL) != 0) { + if (pthread_key_create(&nomem_handler_key, tsd_fini) != 0) { fprintf(stderr, "jq: error: cannot create thread specific key"); abort(); } - if (atexit(tsd_fini) != 0) { - fprintf(stderr, "jq: error: cannot set an exit handler"); - abort(); - } - struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler)); - if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) { - fprintf(stderr, "jq: error: cannot set thread specific data"); - abort(); - } } -void jv_nomem_handler(jv_nomem_handler_f handler, void *data) { +static struct nomem_handler* ensure_mem_handler(void) { pthread_once(&mem_once, tsd_init); // cannot fail struct nomem_handler *nomem_handler; - nomem_handler = pthread_getspecific(nomem_handler_key); if (nomem_handler == NULL) { - handler(data); - fprintf(stderr, "jq: error: cannot allocate memory\n"); - abort(); + nomem_handler = calloc(1, sizeof(struct nomem_handler)); + if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) { + fprintf(stderr, "jq: error: cannot set thread specific data"); + abort(); + } } + return nomem_handler; +} + +void jv_nomem_handler(jv_nomem_handler_f handler, void *data) { + struct nomem_handler *nomem_handler = ensure_mem_handler(); nomem_handler->handler = handler; nomem_handler->data = data; } static void memory_exhausted() { - struct nomem_handler *nomem_handler; - - pthread_once(&mem_once, tsd_init); - nomem_handler = pthread_getspecific(nomem_handler_key); + struct nomem_handler *nomem_handler = ensure_mem_handler(); if (nomem_handler) nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety // Or not