From 47b54cf5a4b6166944efa4ee4cd023d73ebe6c3e Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Fri, 11 Oct 2024 19:10:59 +0200 Subject: types: introduce `ucv_array_sort_r()` and `ucv_object_sort_r()` Introduce two new functions for ordering arrays and object keys which utilize a different compare callback signature and allow passing a user provided pointer to the comparison callback. The main advantages of the `ucv_*_sort_r()` flavors are the ability to pass custom context to comparisons via the user data pointer and the invocation of the comparison callback with direct `uc_value_t *` pointers instead of opaque `const void *` arguments pointing to `uc_value_t *` or json-c internal `struct lh_entry *` pointers respectively. Suggested-by: Isaac de Wolff [align naming and whitespace with the rest of the codebase, rename some variables for clarity, group sort related changes into two commits, drop constness from `uc_value_t *` compare function arguments] Signed-off-by: Jo-Philipp Wich --- types.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) (limited to 'types.c') diff --git a/types.c b/types.c index 26f588e..8c30614 100644 --- a/types.c +++ b/types.c @@ -785,6 +785,28 @@ ucv_array_unshift(uc_value_t *uv, uc_value_t *item) return item; } +typedef struct { + int (*cmp)(uc_value_t *, uc_value_t *, void *); + void *ud; +} array_sort_ctx_t; + +static uc_vector_sort_cb(ucv_array_sort_r_cb, uc_value_t *, array_sort_ctx_t *, { + return ctx->cmp(v1, v2, ctx->ud); +}); + +void +ucv_array_sort_r(uc_value_t *uv, + int (*cmp)(uc_value_t *, uc_value_t *, void *), void *ud) +{ + array_sort_ctx_t ctx = { .cmp = cmp, .ud = ud }; + uc_array_t *array = (uc_array_t *)uv; + + if (ucv_type(uv) != UC_ARRAY || array->count <= 1) + return; + + uc_vector_sort(array, ucv_array_sort_r_cb, &ctx); +} + void ucv_array_sort(uc_value_t *uv, int (*cmp)(const void *, const void *)) { @@ -985,8 +1007,29 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) return true; } -void -ucv_object_sort(uc_value_t *uv, int (*cmp)(const void *, const void *)) + +typedef struct { + int (*cmp)(const void *, const void *); + int (*cmpr)(const char *, uc_value_t *, const char *, uc_value_t *, void *); + void *ud; +} object_sort_ctx_t; + +static uc_vector_sort_cb(ucv_object_sort_cb, const void *, object_sort_ctx_t *, { + (void)v1; + (void)v2; + + return ctx->cmp(k1, k2); +}); + +static uc_vector_sort_cb(ucv_object_sort_r_cb, const struct lh_entry *, object_sort_ctx_t *, { + return ctx->cmpr( + v1 ? lh_entry_k(v1) : NULL, v1 ? lh_entry_v(v1) : NULL, + v2 ? lh_entry_k(v2) : NULL, v2 ? lh_entry_v(v2) : NULL, + ctx->ud); +}); + +static void +ucv_object_sort_common(uc_value_t *uv, object_sort_ctx_t *ctx) { uc_object_t *object = (uc_object_t *)uv; struct lh_table *t; @@ -1007,7 +1050,8 @@ ucv_object_sort(uc_value_t *uv, int (*cmp)(const void *, const void *)) if (!keys.entries) return; - qsort(keys.entries, keys.count, sizeof(keys.entries[0]), cmp); + uc_vector_sort(&keys, + ctx->cmpr ? ucv_object_sort_r_cb : ucv_object_sort_cb, ctx); for (i = 0; i < keys.count; i++) { e = keys.entries[i]; @@ -1027,6 +1071,25 @@ ucv_object_sort(uc_value_t *uv, int (*cmp)(const void *, const void *)) uc_vector_clear(&keys); } +void +ucv_object_sort_r(uc_value_t *uv, + int (*cmp)(const char *, uc_value_t *, + const char *, uc_value_t *, void *), + void *ud) +{ + object_sort_ctx_t ctx = { .cmp = NULL, .cmpr = cmp, .ud = ud }; + + ucv_object_sort_common(uv, &ctx); +} + +void +ucv_object_sort(uc_value_t *uv, int (*cmp)(const void *, const void *)) +{ + object_sort_ctx_t ctx = { .cmp = cmp, .cmpr = NULL, .ud = NULL }; + + ucv_object_sort_common(uv, &ctx); +} + bool ucv_object_delete(uc_value_t *uv, const char *key) { -- cgit v1.2.3 From efeb578065523b2882d71d91f38cd63115928b7a Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sat, 19 Oct 2024 16:06:09 +0200 Subject: types, vm: refactor usage of global variables Introduce an extensible private TLS context structure and use it within libucode to store global state such as active object iterators. This allows using libucode concurrently in multiple threads without unintentionally sharing global state among them. Also adjust the signal dispatching setup logic in `uc_vm_init()` to only enable signal handling if no other VM in the same thread already handles signals. Suggested-by: Isaac de Wolff [squash commits, move signal handler vm pointer and object iterator list into common extensible TLS context, whitespace and naming adjustments, extended signal setup logic] Signed-off-by: Jo-Philipp Wich --- include/ucode/types.h | 18 +++++++++++----- tests/custom/03_stdlib/60_gc | 50 ++++++++++++++++++++++---------------------- types.c | 26 ++++++++++++++++------- vm.c | 28 ++++++++++++++++--------- 4 files changed, 74 insertions(+), 48 deletions(-) (limited to 'types.c') diff --git a/include/ucode/types.h b/include/ucode/types.h index 9cd7cfc..85c3eb8 100644 --- a/include/ucode/types.h +++ b/include/ucode/types.h @@ -206,11 +206,6 @@ typedef struct { uc_declare_vector(uc_resource_types_t, uc_resource_type_t *); - -/* Object iteration */ - -extern uc_list_t uc_object_iterators; - typedef struct { uc_list_t list; struct lh_table *table; @@ -270,6 +265,19 @@ uc_search_path_free(uc_search_path_t *search_path) { } +/* TLS data */ + +typedef struct { + /* VM owning installed signal handlers */ + uc_vm_t *signal_handler_vm; + + /* Object iteration */ + uc_list_t object_iterators; +} uc_thread_context_t; + +__hidden uc_thread_context_t *uc_thread_context_get(void); + + /* VM definitions */ typedef enum { diff --git a/tests/custom/03_stdlib/60_gc b/tests/custom/03_stdlib/60_gc index 28c0d00..44e5d9e 100644 --- a/tests/custom/03_stdlib/60_gc +++ b/tests/custom/03_stdlib/60_gc @@ -45,11 +45,11 @@ Returns an object count if the given operation is `count`. -- End -- -- Expect stdout -- -Count #1: 6 -Count #2: 7 -Count #3: 6 -Count #4: 7 -Count #5: 6 +Count #1: 5 +Count #2: 6 +Count #3: 5 +Count #4: 6 +Count #5: 5 -- End -- @@ -84,24 +84,24 @@ Testing enabling the automatic collector. -- End -- -- Expect stdout -- -Count #1: 7 -Count #2: 14 -Count #3: 14 -Count #4: 14 -Count #5: 14 -Count #6: 14 -Count #7: 14 -Count #8: 14 -Count #9: 14 -Count #10: 14 -Count #11: 14 -Count #12: 24 -Count #13: 34 -Count #14: 44 -Count #15: 54 -Count #16: 64 -Count #17: 74 -Count #18: 84 -Count #19: 94 -Count #20: 104 +Count #1: 6 +Count #2: 12 +Count #3: 12 +Count #4: 12 +Count #5: 12 +Count #6: 12 +Count #7: 12 +Count #8: 12 +Count #9: 12 +Count #10: 12 +Count #11: 12 +Count #12: 22 +Count #13: 32 +Count #14: 42 +Count #15: 52 +Count #16: 62 +Count #17: 72 +Count #18: 82 +Count #19: 92 +Count #20: 102 -- End -- diff --git a/types.c b/types.c index 8c30614..5dbf6a8 100644 --- a/types.c +++ b/types.c @@ -29,11 +29,6 @@ #include "ucode/vm.h" #include "ucode/program.h" -uc_list_t uc_object_iterators = { - .prev = &uc_object_iterators, - .next = &uc_object_iterators -}; - static char *uc_default_search_path[] = { LIB_SEARCH_PATH }; uc_parse_config_t uc_default_parse_config = { @@ -898,7 +893,7 @@ ucv_array_length(uc_value_t *uv) static void ucv_free_object_entry(struct lh_entry *entry) { - uc_list_foreach(item, &uc_object_iterators) { + uc_list_foreach(item, &uc_thread_context_get()->object_iterators) { uc_object_iterator_t *iter = (uc_object_iterator_t *)item; if (iter->u.pos == entry) @@ -955,7 +950,7 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) /* insert will rehash table, backup affected iterator states */ if (rehash) { - uc_list_foreach(item, &uc_object_iterators) { + uc_list_foreach(item, &uc_thread_context_get()->object_iterators) { uc_object_iterator_t *iter = (uc_object_iterator_t *)item; if (iter->table != object->table) @@ -979,7 +974,7 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) /* restore affected iterator state pointer after rehash */ if (rehash) { - uc_list_foreach(item, &uc_object_iterators) { + uc_list_foreach(item, &uc_thread_context_get()->object_iterators) { uc_object_iterator_t *iter = (uc_object_iterator_t *)item; if (iter->table != object->table) @@ -2481,3 +2476,18 @@ uc_search_path_init(uc_search_path_t *search_path) for (i = 0; i < ARRAY_SIZE(uc_default_search_path); i++) uc_vector_push(search_path, xstrdup(uc_default_search_path[i])); } + + +static __thread uc_thread_context_t *tls_ctx; + +uc_thread_context_t * +uc_thread_context_get(void) +{ + if (tls_ctx == NULL) { + tls_ctx = xalloc(sizeof(*tls_ctx)); + tls_ctx->object_iterators.prev = &tls_ctx->object_iterators; + tls_ctx->object_iterators.next = &tls_ctx->object_iterators; + } + + return tls_ctx; +} diff --git a/vm.c b/vm.c index 9488288..ce9c217 100644 --- a/vm.c +++ b/vm.c @@ -145,37 +145,44 @@ uc_vm_alloc_global_scope(uc_vm_t *vm) static void uc_vm_output_exception(uc_vm_t *vm, uc_exception_t *ex); -static uc_vm_t *signal_handler_vm; - static void uc_vm_signal_handler(int sig) { - assert(signal_handler_vm); + uc_vm_t *vm = uc_thread_context_get()->signal_handler_vm; + + assert(vm); - uc_vm_signal_raise(signal_handler_vm, sig); + uc_vm_signal_raise(vm, sig); } static void uc_vm_signal_handlers_setup(uc_vm_t *vm) { + uc_thread_context_t *tctx; + memset(&vm->signal, 0, sizeof(vm->signal)); vm->signal.sigpipe[0] = -1; vm->signal.sigpipe[1] = -1; - if (!vm->config->setup_signal_handlers) + if (vm->config->setup_signal_handlers) return; - if (pipe2(vm->signal.sigpipe, O_CLOEXEC | O_NONBLOCK) != 0) + tctx = uc_thread_context_get(); + + if (tctx->signal_handler_vm) return; - signal_handler_vm = vm; + if (pipe2(vm->signal.sigpipe, O_CLOEXEC | O_NONBLOCK) != 0) + return; vm->signal.handler = ucv_array_new_length(vm, UC_SYSTEM_SIGNAL_COUNT); vm->signal.sa.sa_handler = uc_vm_signal_handler; vm->signal.sa.sa_flags = SA_RESTART | SA_ONSTACK; sigemptyset(&vm->signal.sa.sa_mask); + + tctx->signal_handler_vm = vm; } void uc_vm_init(uc_vm_t *vm, uc_parse_config_t *config) @@ -778,11 +785,10 @@ uc_vm_exception_tostring(uc_vm_t *vm, size_t nargs) return message ? ucv_get(message) : ucv_string_new("Exception"); } -static uc_value_t *exception_prototype = NULL; - static uc_value_t * uc_vm_exception_new(uc_vm_t *vm, uc_exception_type_t type, const char *message, uc_value_t *stacktrace) { + uc_value_t *exception_prototype = uc_vm_registry_get(vm, "vm.exception.proto"); uc_value_t *exo; if (exception_prototype == NULL) { @@ -790,6 +796,8 @@ uc_vm_exception_new(uc_vm_t *vm, uc_exception_type_t type, const char *message, ucv_object_add(exception_prototype, "tostring", ucv_cfunction_new("tostring", uc_vm_exception_tostring)); + + uc_vm_registry_set(vm, "vm.exception.proto", exception_prototype); } exo = ucv_object_new(vm); @@ -2222,7 +2230,7 @@ uc_vm_object_iterator_next(uc_vm_t *vm, uc_vm_insn_t insn, iter->table = obj->table; iter->u.pos = obj->table->head; - uc_list_insert(&uc_object_iterators, &iter->list); + uc_list_insert(&uc_thread_context_get()->object_iterators, &iter->list); } else if (ucv_type(k) == UC_RESOURCE && res->type == &uc_vm_object_iterator_type && res->data != NULL) { -- cgit v1.2.3