ucode: add patches to improve exception handling for ubus/uloop
Add API to allow setting an exception handler for user provided callbacks Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
87f130a6ae
commit
185b48e330
3 changed files with 403 additions and 0 deletions
|
@ -0,0 +1,47 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 11 Jun 2025 17:35:00 +0200
|
||||
Subject: [PATCH] vm: export function for converting exception to ucode
|
||||
value
|
||||
|
||||
This can be used to simplify implementing module execption handlers in
|
||||
ucode.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/ucode/vm.h
|
||||
+++ b/include/ucode/vm.h
|
||||
@@ -152,6 +152,7 @@ uc_exception_type_t uc_vm_call(uc_vm_t *
|
||||
|
||||
void __attribute__((format(printf, 3, 0)))
|
||||
uc_vm_raise_exception(uc_vm_t *vm, uc_exception_type_t type, const char *fmt, ...);
|
||||
+uc_value_t *uc_vm_exception_value(uc_vm_t *vm);
|
||||
|
||||
uc_vm_status_t uc_vm_execute(uc_vm_t *vm, uc_program_t *fn, uc_value_t **retval);
|
||||
uc_value_t *uc_vm_invoke(uc_vm_t *vm, const char *fname, size_t nargs, ...);
|
||||
--- a/vm.c
|
||||
+++ b/vm.c
|
||||
@@ -814,9 +814,12 @@ uc_vm_exception_tostring(uc_vm_t *vm, si
|
||||
return message ? ucv_get(message) : ucv_string_new("Exception");
|
||||
}
|
||||
|
||||
-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 *
|
||||
+uc_vm_exception_value(uc_vm_t *vm)
|
||||
{
|
||||
+ uc_exception_type_t type = vm->exception.type;
|
||||
+ const char *message = vm->exception.message;
|
||||
+ uc_value_t *stacktrace = vm->exception.stacktrace;
|
||||
uc_value_t *exception_prototype = uc_vm_registry_get(vm, "vm.exception.proto");
|
||||
uc_value_t *exo;
|
||||
|
||||
@@ -881,7 +884,7 @@ uc_vm_handle_exception(uc_vm_t *vm)
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
|
||||
/* prepare exception object and expose it to user handler code */
|
||||
- exo = uc_vm_exception_new(vm, vm->exception.type, vm->exception.message, vm->exception.stacktrace);
|
||||
+ exo = uc_vm_exception_value(vm);
|
||||
|
||||
uc_vm_stack_push(vm, exo);
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 11 Jun 2025 18:31:39 +0200
|
||||
Subject: [PATCH] uloop: add exception_handler_set function
|
||||
|
||||
This allows calling the provided handler on exceptions, avoiding the
|
||||
existing behavior of calling uloop_end().
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/lib/uloop.c
|
||||
+++ b/lib/uloop.c
|
||||
@@ -114,27 +114,48 @@ uc_uloop_cb_free(uc_uloop_cb_t *cb)
|
||||
}
|
||||
|
||||
static bool
|
||||
+uc_uloop_vm_call(uc_vm_t *vm, bool mcall, size_t nargs)
|
||||
+{
|
||||
+ uc_value_t *exh, *val;
|
||||
+
|
||||
+ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE)
|
||||
+ return true;
|
||||
+
|
||||
+ exh = uc_vm_registry_get(vm, "uloop.ex_handler");
|
||||
+ if (!ucv_is_callable(exh))
|
||||
+ goto error;
|
||||
+
|
||||
+ val = uc_vm_exception_value(vm);
|
||||
+ uc_vm_stack_push(vm, ucv_get(exh));
|
||||
+ uc_vm_stack_push(vm, ucv_get(val));
|
||||
+
|
||||
+ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE)
|
||||
+ goto error;
|
||||
+
|
||||
+ ucv_put(uc_vm_stack_pop(vm));
|
||||
+
|
||||
+ return false;
|
||||
+
|
||||
+error:
|
||||
+ uloop_end();
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
uc_uloop_cb_invoke(uc_uloop_cb_t *cb, uc_value_t *arg)
|
||||
{
|
||||
uc_vm_t *vm = cb->vm;
|
||||
uc_value_t *func = ucv_resource_value_get(cb->obj, 0);
|
||||
|
||||
if (!ucv_is_callable(func))
|
||||
- return false;
|
||||
+ return;
|
||||
|
||||
uc_vm_stack_push(vm, ucv_get(cb->obj));
|
||||
uc_vm_stack_push(vm, ucv_get(func));
|
||||
uc_vm_stack_push(vm, ucv_get(arg));
|
||||
|
||||
- if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
|
||||
- uloop_end();
|
||||
-
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- ucv_put(uc_vm_stack_pop(vm));
|
||||
-
|
||||
- return true;
|
||||
+ if (uc_uloop_vm_call(vm, true, 1))
|
||||
+ ucv_put(uc_vm_stack_pop(vm));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1550,11 +1571,8 @@ uc_uloop_task_output_cb(struct uloop_fd
|
||||
uc_vm_stack_push(vm, ucv_get(obj));
|
||||
uc_vm_stack_push(vm, ucv_get(task->input_cb));
|
||||
|
||||
- if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) {
|
||||
- uloop_end();
|
||||
-
|
||||
+ if (!uc_uloop_vm_call(vm, true, 0))
|
||||
return;
|
||||
- }
|
||||
|
||||
msg = uc_vm_stack_pop(vm);
|
||||
uc_uloop_pipe_send_common(vm, msg, task->input_fd);
|
||||
@@ -1572,14 +1590,10 @@ uc_uloop_task_output_cb(struct uloop_fd
|
||||
uc_vm_stack_push(vm, ucv_get(task->output_cb));
|
||||
uc_vm_stack_push(vm, msg);
|
||||
|
||||
- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) {
|
||||
- ucv_put(uc_vm_stack_pop(vm));
|
||||
- }
|
||||
- else {
|
||||
- uloop_end();
|
||||
-
|
||||
+ if (!uc_uloop_vm_call(vm, true, 1))
|
||||
return;
|
||||
- }
|
||||
+
|
||||
+ ucv_put(uc_vm_stack_pop(vm));
|
||||
}
|
||||
else {
|
||||
ucv_put(msg);
|
||||
@@ -1703,7 +1717,7 @@ uc_uloop_task(uc_vm_t *vm, size_t nargs)
|
||||
uc_vm_stack_push(vm, func);
|
||||
uc_vm_stack_push(vm, ucv_get(p));
|
||||
|
||||
- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) {
|
||||
+ if (uc_uloop_vm_call(vm, false, 1)) {
|
||||
res = uc_vm_stack_pop(vm);
|
||||
uc_uloop_pipe_send_common(vm, res, tpipe->output);
|
||||
ucv_put(res);
|
||||
@@ -2167,6 +2181,19 @@ uc_uloop_signal(uc_vm_t *vm, size_t narg
|
||||
}
|
||||
#endif
|
||||
|
||||
+static uc_value_t *
|
||||
+uc_uloop_exception_handler_set(uc_vm_t *vm, size_t nargs)
|
||||
+{
|
||||
+ uc_value_t *arg = uc_fn_arg(0);
|
||||
+
|
||||
+ if (arg && !ucv_is_callable(arg))
|
||||
+ return NULL;
|
||||
+
|
||||
+ uc_vm_registry_set(vm, "uloop.ex_handler", ucv_get(arg));
|
||||
+
|
||||
+ return ucv_boolean_new(true);
|
||||
+}
|
||||
+
|
||||
|
||||
static const uc_function_list_t timer_fns[] = {
|
||||
{ "set", uc_uloop_timer_set },
|
||||
@@ -2233,6 +2260,7 @@ static const uc_function_list_t global_f
|
||||
#ifdef HAVE_ULOOP_SIGNAL
|
||||
{ "signal", uc_uloop_signal },
|
||||
#endif
|
||||
+ { "exception_handler_set", uc_uloop_exception_handler_set },
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 11 Jun 2025 18:40:10 +0200
|
||||
Subject: [PATCH] ubus: add exception_handler_set function
|
||||
|
||||
This allows calling the provided handler on exceptions, avoiding the
|
||||
existing behavior of calling uloop_end().
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/lib/ubus.c
|
||||
+++ b/lib/ubus.c
|
||||
@@ -573,6 +573,40 @@ uc_ubus_call_cb(struct ubus_request *req
|
||||
}
|
||||
|
||||
static void
|
||||
+uc_ubus_vm_handle_exception(uc_vm_t *vm)
|
||||
+{
|
||||
+ uc_value_t *exh, *val;
|
||||
+
|
||||
+ exh = uc_vm_registry_get(vm, "ubus.ex_handler");
|
||||
+ if (!ucv_is_callable(exh))
|
||||
+ goto error;
|
||||
+
|
||||
+ val = uc_vm_exception_value(vm);
|
||||
+ uc_vm_stack_push(vm, ucv_get(exh));
|
||||
+ uc_vm_stack_push(vm, ucv_get(val));
|
||||
+
|
||||
+ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE)
|
||||
+ goto error;
|
||||
+
|
||||
+ ucv_put(uc_vm_stack_pop(vm));
|
||||
+ return;
|
||||
+
|
||||
+error:
|
||||
+ uloop_end();
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+uc_ubus_vm_call(uc_vm_t *vm, bool mcall, size_t nargs)
|
||||
+{
|
||||
+ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE)
|
||||
+ return true;
|
||||
+
|
||||
+ uc_ubus_vm_handle_exception(vm);
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply)
|
||||
{
|
||||
uc_value_t *this = ucv_get(defer->res);
|
||||
@@ -587,7 +621,7 @@ uc_ubus_call_user_cb(uc_ubus_deferred_t
|
||||
uc_vm_stack_push(vm, ucv_int64_new(ret));
|
||||
uc_vm_stack_push(vm, ucv_get(reply));
|
||||
|
||||
- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 2))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
}
|
||||
|
||||
@@ -623,10 +657,8 @@ uc_ubus_call_data_user_cb(struct ubus_re
|
||||
uc_vm_stack_push(vm, ucv_get(func));
|
||||
uc_vm_stack_push(vm, ucv_get(reply));
|
||||
|
||||
- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 1))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,10 +677,8 @@ uc_ubus_call_fd_cb(struct ubus_request *
|
||||
uc_vm_stack_push(vm, ucv_get(func));
|
||||
uc_vm_stack_push(vm, ucv_int64_new(fd));
|
||||
|
||||
- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 1))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,10 +957,8 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu
|
||||
uc_vm_stack_push(vm, ucv_get(replycb));
|
||||
uc_vm_stack_push(vm, ucv_int64_new(rv));
|
||||
|
||||
- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, false, 1))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
|
||||
ucv_put(res->res);
|
||||
}
|
||||
@@ -1199,10 +1227,8 @@ uc_ubus_object_notify_data_cb(struct ubu
|
||||
uc_vm_stack_push(vm, ucv_int64_new(type));
|
||||
uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true));
|
||||
|
||||
- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 2))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1222,10 +1248,8 @@ uc_ubus_object_notify_status_cb(struct u
|
||||
uc_vm_stack_push(vm, ucv_int64_new(idx));
|
||||
uc_vm_stack_push(vm, ucv_int64_new(ret));
|
||||
|
||||
- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 2))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1245,10 +1269,8 @@ uc_ubus_object_notify_complete_cb(struct
|
||||
uc_vm_stack_push(vm, ucv_int64_new(idx));
|
||||
uc_vm_stack_push(vm, ucv_int64_new(ret));
|
||||
|
||||
- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 2))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
|
||||
notifyctx->complete = true;
|
||||
@@ -1579,7 +1601,7 @@ uc_ubus_handle_reply_common(struct ubus_
|
||||
/* treat other exceptions as fatal and halt uloop */
|
||||
default:
|
||||
uc_ubus_request_finish_common(callctx, UBUS_STATUS_UNKNOWN_ERROR);
|
||||
- uloop_end();
|
||||
+ uc_ubus_vm_handle_exception(vm);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1638,10 +1660,8 @@ uc_ubus_object_subscribe_cb(struct ubus_
|
||||
uc_vm_stack_push(uuobj->vm, ucv_get(uuobj->res));
|
||||
uc_vm_stack_push(uuobj->vm, ucv_get(func));
|
||||
|
||||
- if (uc_vm_call(uuobj->vm, true, 0) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(uuobj->vm, true, 0))
|
||||
ucv_put(uc_vm_stack_pop(uuobj->vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -1917,10 +1937,8 @@ uc_ubus_listener_cb(struct ubus_context
|
||||
uc_vm_stack_push(vm, ucv_string_new(type));
|
||||
uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true));
|
||||
|
||||
- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 2))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
|
||||
static uc_value_t *
|
||||
@@ -2040,10 +2058,8 @@ uc_ubus_subscriber_remove_cb(struct ubus
|
||||
uc_vm_stack_push(vm, ucv_get(func));
|
||||
uc_vm_stack_push(vm, ucv_uint64_new(id));
|
||||
|
||||
- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(vm, true, 1))
|
||||
ucv_put(uc_vm_stack_pop(vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
|
||||
static uc_value_t *
|
||||
@@ -2334,10 +2350,8 @@ uc_ubus_channel_disconnect_cb(struct ubu
|
||||
uc_vm_stack_push(c->vm, ucv_get(c->res));
|
||||
uc_vm_stack_push(c->vm, ucv_get(func));
|
||||
|
||||
- if (uc_vm_call(c->vm, true, 0) == EXCEPTION_NONE)
|
||||
+ if (uc_ubus_vm_call(c->vm, true, 0))
|
||||
ucv_put(uc_vm_stack_pop(c->vm));
|
||||
- else
|
||||
- uloop_end();
|
||||
}
|
||||
|
||||
blob_buf_free(&c->buf);
|
||||
@@ -2438,10 +2452,25 @@ uc_ubus_channel_connect(uc_vm_t *vm, siz
|
||||
}
|
||||
|
||||
|
||||
+static uc_value_t *
|
||||
+uc_ubus_exception_handler_set(uc_vm_t *vm, size_t nargs)
|
||||
+{
|
||||
+ uc_value_t *arg = uc_fn_arg(0);
|
||||
+
|
||||
+ if (arg && !ucv_is_callable(arg))
|
||||
+ return NULL;
|
||||
+
|
||||
+ uc_vm_registry_set(vm, "ubus.ex_handler", ucv_get(arg));
|
||||
+
|
||||
+ return ucv_boolean_new(true);
|
||||
+}
|
||||
+
|
||||
+
|
||||
static const uc_function_list_t global_fns[] = {
|
||||
{ "error", uc_ubus_error },
|
||||
{ "connect", uc_ubus_connect },
|
||||
{ "open_channel", uc_ubus_channel_connect },
|
||||
+ { "exception_handler_set", uc_ubus_exception_handler_set },
|
||||
};
|
||||
|
||||
static const uc_function_list_t conn_fns[] = {
|
Loading…
Reference in a new issue