You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
6.9 KiB
244 lines
6.9 KiB
commit fead9669cb67badb22789d3ed1888779ed85c679 |
|
Author: Tomas Halman <thalman@redhat.com> |
|
Date: Thu Mar 9 11:34:44 2023 +0100 |
|
|
|
Test that jq works in threads |
|
|
|
This patch implements test that searches a key in simple |
|
json in pthread. |
|
|
|
diff -up jq-1.6/src/jq_test.c.orig jq-1.6/src/jq_test.c |
|
--- jq-1.6/src/jq_test.c.orig 2023-03-09 14:02:16.980062057 +0100 |
|
+++ jq-1.6/src/jq_test.c 2023-03-09 14:03:38.347017032 +0100 |
|
@@ -2,12 +2,17 @@ |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
+#ifdef HAVE_PTHREAD |
|
+#include <pthread.h> |
|
+#endif |
|
#include "jv.h" |
|
#include "jq.h" |
|
|
|
static void jv_test(); |
|
static void run_jq_tests(jv, int, FILE *, int, int); |
|
- |
|
+#ifdef HAVE_PTHREAD |
|
+static void run_jq_pthread_tests(); |
|
+#endif |
|
|
|
int jq_testsuite(jv libdirs, int verbose, int argc, char* argv[]) { |
|
FILE *testdata = stdin; |
|
@@ -32,6 +37,9 @@ int jq_testsuite(jv libdirs, int verbose |
|
} |
|
} |
|
run_jq_tests(libdirs, verbose, testdata, skip, take); |
|
+#ifdef HAVE_PTHREAD |
|
+ run_jq_pthread_tests(); |
|
+#endif |
|
return 0; |
|
} |
|
|
|
@@ -105,7 +113,7 @@ static void run_jq_tests(jv lib_dirs, in |
|
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) |
|
break; |
|
} |
|
- |
|
+ |
|
must_fail = 0; |
|
check_msg = 0; |
|
|
|
@@ -229,7 +237,7 @@ static void run_jq_tests(jv lib_dirs, in |
|
total_skipped = tests_to_skip - skip; |
|
} |
|
|
|
- printf("%d of %d tests passed (%d malformed, %d skipped)\n", |
|
+ printf("%d of %d tests passed (%d malformed, %d skipped)\n", |
|
passed, tests, invalid, total_skipped); |
|
|
|
if (skip > 0) { |
|
@@ -241,6 +249,88 @@ static void run_jq_tests(jv lib_dirs, in |
|
} |
|
|
|
|
|
+/// pthread regression test |
|
+#ifdef HAVE_PTHREAD |
|
+#define NUMBER_OF_THREADS 3 |
|
+struct test_pthread_data { |
|
+ int result; |
|
+}; |
|
+ |
|
+static int test_pthread_jq_parse(jq_state *jq, struct jv_parser *parser) |
|
+{ |
|
+ int rv = 0; |
|
+ jv value; |
|
+ |
|
+ value = jv_parser_next(parser); |
|
+ while (jv_is_valid(value)) { |
|
+ jq_start(jq, value, 0); |
|
+ jv result = jq_next(jq); |
|
+ |
|
+ while (jv_is_valid(result)) { |
|
+ jv_free(result); |
|
+ result = jq_next(jq); |
|
+ } |
|
+ jv_free(result); |
|
+ value = jv_parser_next(parser); |
|
+ } |
|
+ jv_free(value); |
|
+ return rv; |
|
+} |
|
+ |
|
+static void *test_pthread_run(void *ptr) { |
|
+ int rv; |
|
+ jq_state *jq; |
|
+ const char *prg = ".data"; |
|
+ const char *buf = "{ \"data\": 1 }"; |
|
+ struct test_pthread_data *data = ptr; |
|
+ |
|
+ jq = jq_init(); |
|
+ if (jq_compile(jq, prg) == 0) { |
|
+ jq_teardown(&jq); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ struct jv_parser *parser = jv_parser_new(0); |
|
+ jv_parser_set_buf(parser, buf, strlen(buf), 0); |
|
+ rv = test_pthread_jq_parse(jq, parser); |
|
+ |
|
+ data->result = rv; |
|
+ |
|
+ jv_parser_free(parser); |
|
+ jq_teardown(&jq); |
|
+ return NULL; |
|
+} |
|
+ |
|
+static void run_jq_pthread_tests() { |
|
+ pthread_t threads[NUMBER_OF_THREADS]; |
|
+ struct test_pthread_data data[NUMBER_OF_THREADS]; |
|
+ int createerror; |
|
+ int a; |
|
+ |
|
+ memset(&threads, 0, sizeof(threads)); |
|
+ memset(&data, 0, sizeof(data)); |
|
+ |
|
+ // Create all threads |
|
+ for (a = 0; a < NUMBER_OF_THREADS; ++a) { |
|
+ createerror = pthread_create(&threads[a], NULL, test_pthread_run, &data[a]); |
|
+ assert(createerror == 0); |
|
+ } |
|
+ |
|
+ // wait for all threads |
|
+ for(a = 0; a < NUMBER_OF_THREADS; ++a) { |
|
+ if (threads[a] != 0) { |
|
+ pthread_join(threads[a], NULL); |
|
+ } |
|
+ } |
|
+ |
|
+ // check results |
|
+ for(a = 0; a < NUMBER_OF_THREADS; ++a) { |
|
+ assert(data[a].result == 0); |
|
+ } |
|
+} |
|
+#endif // HAVE_PTHREAD |
|
+ |
|
+ |
|
static void jv_test() { |
|
/// JSON parser regression tests |
|
{ |
|
|
|
commit f05c5bb521f404592b137e02a1b8abd50da87ca3 |
|
Author: Tomas Halman <thalman@redhat.com> |
|
Date: Wed Mar 1 20:07:28 2023 +0100 |
|
|
|
Fix segmentation fault when using jq in threads |
|
|
|
In previous code nomem_handler in jv_alloc.c is called only once |
|
and therefore the structure is not initialized for second and |
|
following threads. |
|
|
|
This leads to segmentation fault in multi-threading environment. |
|
|
|
This patch moves initialization of nomem_handler out of the |
|
pthread_once call. |
|
|
|
diff -up jq-1.6/src/jv_alloc.c.orig jq-1.6/src/jv_alloc.c |
|
--- jq-1.6/src/jv_alloc.c.orig 2018-11-02 02:49:29.000000000 +0100 |
|
+++ jq-1.6/src/jv_alloc.c 2023-03-09 14:13:47.810177037 +0100 |
|
@@ -45,9 +45,11 @@ static void memory_exhausted() { |
|
#ifdef HAVE_PTHREAD_KEY_CREATE |
|
#include <pthread.h> |
|
|
|
-pthread_key_t nomem_handler_key; |
|
-pthread_once_t mem_once = PTHREAD_ONCE_INIT; |
|
+static pthread_key_t nomem_handler_key; |
|
+static pthread_once_t mem_once = PTHREAD_ONCE_INIT; |
|
|
|
+/* tsd_fini is called on application exit */ |
|
+/* it clears the nomem_handler allocated in the main thread */ |
|
static void tsd_fini(void) { |
|
struct nomem_handler *nomem_handler; |
|
nomem_handler = pthread_getspecific(nomem_handler_key); |
|
@@ -57,30 +59,45 @@ static void tsd_fini(void) { |
|
} |
|
} |
|
|
|
+/* The tsd_fini_thread is a destructor set by calling */ |
|
+/* pthread_key_create(&nomem_handler_key, tsd_fini_thread) */ |
|
+/* It is called when thread ends */ |
|
+static void tsd_fini_thread(void *nomem_handler) { |
|
+ free(nomem_handler); |
|
+} |
|
+ |
|
static void tsd_init(void) { |
|
- if (pthread_key_create(&nomem_handler_key, NULL) != 0) { |
|
- fprintf(stderr, "error: cannot create thread specific key"); |
|
+ if (pthread_key_create(&nomem_handler_key, tsd_fini_thread) != 0) { |
|
+ fprintf(stderr, "jq: error: cannot create thread specific key"); |
|
abort(); |
|
} |
|
if (atexit(tsd_fini) != 0) { |
|
- fprintf(stderr, "error: cannot set an exit handler"); |
|
+ 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, "error: cannot set thread specific data"); |
|
- abort(); |
|
+} |
|
+ |
|
+static void tsd_init_nomem_handler(void) |
|
+{ |
|
+ if (pthread_getspecific(nomem_handler_key) == NULL) { |
|
+ 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) { |
|
pthread_once(&mem_once, tsd_init); // cannot fail |
|
+ tsd_init_nomem_handler(); |
|
+ |
|
struct nomem_handler *nomem_handler; |
|
|
|
nomem_handler = pthread_getspecific(nomem_handler_key); |
|
if (nomem_handler == NULL) { |
|
handler(data); |
|
- fprintf(stderr, "error: cannot allocate memory\n"); |
|
+ fprintf(stderr, "jq: error: cannot allocate memory\n"); |
|
abort(); |
|
} |
|
nomem_handler->handler = handler; |
|
@@ -91,6 +108,8 @@ static void memory_exhausted() { |
|
struct nomem_handler *nomem_handler; |
|
|
|
pthread_once(&mem_once, tsd_init); |
|
+ tsd_init_nomem_handler(); |
|
+ |
|
nomem_handler = pthread_getspecific(nomem_handler_key); |
|
if (nomem_handler) |
|
nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety
|
|
|