245 lines
6.9 KiB
Diff
245 lines
6.9 KiB
Diff
|
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
|