Skip to content

Commit

Permalink
uloop: support new interval and signal APIs
Browse files Browse the repository at this point in the history
Add bindings for the new uloop interval and signal handling APIs and extend
the CMake logic to properly detect the presence of these facilities.

Signed-off-by: Jo-Philipp Wich <[email protected]>
  • Loading branch information
jow- committed Nov 3, 2023
1 parent cfb24ea commit 0352a33
Show file tree
Hide file tree
Showing 2 changed files with 303 additions and 2 deletions.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,18 @@ if(ULOOP_SUPPORT)
target_link_options(uloop_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS})
set(CMAKE_REQUIRED_LIBRARIES ${libubox})
check_function_exists(uloop_timeout_remaining64 REMAINING64_FUNCTION_EXISTS)
check_function_exists(uloop_interval_set INTERVAL_FUNCTION_EXISTS)
check_function_exists(uloop_signal_add SIGNAL_FUNCTION_EXISTS)
unset(CMAKE_REQUIRED_LIBRARIES)
if(REMAINING64_FUNCTION_EXISTS)
target_compile_definitions(uloop_lib PUBLIC HAVE_ULOOP_TIMEOUT_REMAINING64)
endif()
if(INTERVAL_FUNCTION_EXISTS)
target_compile_definitions(uloop_lib PUBLIC HAVE_ULOOP_INTERVAL)
endif()
if(SIGNAL_FUNCTION_EXISTS)
target_compile_definitions(uloop_lib PUBLIC HAVE_ULOOP_SIGNAL)
endif()
target_link_libraries(uloop_lib ${libubox})
endif()

Expand Down
297 changes: 295 additions & 2 deletions lib/uloop.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
#define err_return(err) do { last_error = err; return NULL; } while(0)

static uc_resource_type_t *timer_type, *handle_type, *process_type, *task_type, *pipe_type;

#ifdef HAVE_ULOOP_INTERVAL
static uc_resource_type_t *interval_type;
#endif

#ifdef HAVE_ULOOP_SIGNAL
static uc_resource_type_t *signal_type;
#endif

static uc_value_t *object_registry;

static int last_error = 0;
Expand Down Expand Up @@ -1042,6 +1051,233 @@ uc_uloop_task(uc_vm_t *vm, size_t nargs)
}


#ifdef HAVE_ULOOP_INTERVAL
typedef struct {
struct uloop_interval interval;
size_t registry_index;
uc_vm_t *vm;
} uc_uloop_interval_t;

static void
uc_uloop_interval_clear(uc_uloop_interval_t **interval)
{
/* drop registry entries and clear data to prevent reuse */
uc_uloop_reg_remove((*interval)->registry_index);
free(*interval);
*interval = NULL;
}

static uc_value_t *
uc_uloop_interval_set(uc_vm_t *vm, size_t nargs)
{
uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");
uc_value_t *timeout = uc_fn_arg(0);
int t, rv;

if (!interval || !*interval)
err_return(EINVAL);

errno = 0;
t = timeout ? (int)ucv_int64_get(timeout) : -1;

if (errno)
err_return(errno);

rv = uloop_interval_set(&(*interval)->interval, t);

return ucv_boolean_new(rv == 0);
}

static uc_value_t *
uc_uloop_interval_remaining(uc_vm_t *vm, size_t nargs)
{
uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");

if (!interval || !*interval)
err_return(EINVAL);

return ucv_int64_new(uloop_interval_remaining(&(*interval)->interval));
}

static uc_value_t *
uc_uloop_interval_expirations(uc_vm_t *vm, size_t nargs)
{
uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");

if (!interval || !*interval)
err_return(EINVAL);

return ucv_int64_new((*interval)->interval.expirations);
}

static uc_value_t *
uc_uloop_interval_cancel(uc_vm_t *vm, size_t nargs)
{
uc_uloop_interval_t **interval = uc_fn_this("uloop.interval");
int rv;

if (!interval || !*interval)
err_return(EINVAL);

rv = uloop_interval_cancel(&(*interval)->interval);

uc_uloop_interval_clear(interval);

return ucv_boolean_new(rv == 0);
}

static void
uc_uloop_interval_cb(struct uloop_interval *uintv)
{
uc_uloop_interval_t *interval = (uc_uloop_interval_t *)uintv;

uc_uloop_reg_invoke(interval->vm, interval->registry_index, NULL);
}

static uc_value_t *
uc_uloop_interval(uc_vm_t *vm, size_t nargs)
{
uc_value_t *timeout = uc_fn_arg(0);
uc_value_t *callback = uc_fn_arg(1);
uc_uloop_interval_t *interval;
uc_value_t *res;
int t;

errno = 0;
t = timeout ? ucv_int64_get(timeout) : -1;

if (errno)
err_return(errno);

if (!ucv_is_callable(callback))
err_return(EINVAL);

interval = xalloc(sizeof(*interval));
interval->interval.cb = uc_uloop_interval_cb;
interval->vm = vm;

if (t >= 0)
uloop_interval_set(&interval->interval, t);

res = uc_resource_new(interval_type, interval);

interval->registry_index = uc_uloop_reg_add(res, callback);

return res;
}
#endif

#ifdef HAVE_ULOOP_SIGNAL
typedef struct {
struct uloop_signal signal;
size_t registry_index;
uc_vm_t *vm;
} uc_uloop_signal_t;

static void
uc_uloop_signal_clear(uc_uloop_signal_t **signal)
{
/* drop registry entries and clear data to prevent reuse */
uc_uloop_reg_remove((*signal)->registry_index);
free(*signal);
*signal = NULL;
}

static uc_value_t *
uc_uloop_signal_signo(uc_vm_t *vm, size_t nargs)
{
uc_uloop_signal_t **signal = uc_fn_this("uloop.signal");

if (!signal || !*signal)
err_return(EINVAL);

return ucv_int64_new((*signal)->signal.signo);
}

static uc_value_t *
uc_uloop_signal_delete(uc_vm_t *vm, size_t nargs)
{
uc_uloop_signal_t **signal = uc_fn_this("uloop.signal");
int rv;

if (!signal || !*signal)
err_return(EINVAL);

rv = uloop_signal_delete(&(*signal)->signal);

uc_uloop_signal_clear(signal);

if (rv != 0)
err_return(EINVAL);

return ucv_boolean_new(true);
}

static void
uc_uloop_signal_cb(struct uloop_signal *usig)
{
uc_uloop_signal_t *signal = (uc_uloop_signal_t *)usig;

uc_uloop_reg_invoke(signal->vm, signal->registry_index, NULL);
}

static int
parse_signo(uc_value_t *sigspec)
{
if (ucv_type(sigspec) == UC_STRING) {
const char *signame = ucv_string_get(sigspec);

if (!strncasecmp(signame, "SIG", 3))
signame += 3;

for (size_t i = 0; i < UC_SYSTEM_SIGNAL_COUNT; i++) {
if (!uc_system_signal_names[i])
continue;

if (strcasecmp(uc_system_signal_names[i], signame))
continue;

return i;
}
}

uc_value_t *signum = ucv_to_number(sigspec);
int64_t signo = ucv_int64_get(signum);
ucv_put(signum);

if (signo < 1 || signo >= UC_SYSTEM_SIGNAL_COUNT)
return -1;

return signo;
}

static uc_value_t *
uc_uloop_signal(uc_vm_t *vm, size_t nargs)
{
int signo = parse_signo(uc_fn_arg(0));
uc_value_t *callback = uc_fn_arg(1);
uc_uloop_signal_t *signal;
uc_value_t *res;

if (signo == -1 || !ucv_is_callable(callback))
err_return(EINVAL);

signal = xalloc(sizeof(*signal));
signal->signal.signo = signo;
signal->signal.cb = uc_uloop_signal_cb;
signal->vm = vm;

uloop_signal_add(&signal->signal);

res = uc_resource_new(signal_type, signal);

signal->registry_index = uc_uloop_reg_add(res, callback);

return res;
}
#endif


static const uc_function_list_t timer_fns[] = {
{ "set", uc_uloop_timer_set },
{ "remaining", uc_uloop_timer_remaining },
Expand Down Expand Up @@ -1072,6 +1308,23 @@ static const uc_function_list_t pipe_fns[] = {
{ "receiving", uc_uloop_pipe_receiving },
};

#ifdef HAVE_ULOOP_INTERVAL
static const uc_function_list_t interval_fns[] = {
{ "set", uc_uloop_interval_set },
{ "remaining", uc_uloop_interval_remaining },
{ "expirations",
uc_uloop_interval_expirations },
{ "cancel", uc_uloop_interval_cancel },
};
#endif

#ifdef HAVE_ULOOP_SIGNAL
static const uc_function_list_t signal_fns[] = {
{ "signo", uc_uloop_signal_signo },
{ "delete", uc_uloop_signal_delete },
};
#endif

static const uc_function_list_t global_fns[] = {
{ "error", uc_uloop_error },
{ "init", uc_uloop_init },
Expand All @@ -1084,6 +1337,12 @@ static const uc_function_list_t global_fns[] = {
{ "running", uc_uloop_running },
{ "done", uc_uloop_done },
{ "end", uc_uloop_end },
#ifdef HAVE_ULOOP_INTERVAL
{ "interval", uc_uloop_interval },
#endif
#ifdef HAVE_ULOOP_SIGNAL
{ "signal", uc_uloop_signal },
#endif
};


Expand Down Expand Up @@ -1150,14 +1409,40 @@ static void close_pipe(void *ud)
free(pipe);
}

#ifdef HAVE_ULOOP_INTERVAL
static void close_interval(void *ud)
{
uc_uloop_interval_t *interval = ud;

if (!interval)
return;

uloop_interval_cancel(&interval->interval);
free(interval);
}
#endif

#ifdef HAVE_ULOOP_SIGNAL
static void close_signal(void *ud)
{
uc_uloop_signal_t *signal = ud;

if (!signal)
return;

uloop_signal_delete(&signal->signal);
free(signal);
}
#endif


static struct {
struct uloop_fd ufd;
uc_vm_t *vm;
} signal_handle;

static void
uc_uloop_signal_cb(struct uloop_fd *ufd, unsigned int events)
uc_uloop_vm_signal_cb(struct uloop_fd *ufd, unsigned int events)
{
if (uc_vm_signal_dispatch(signal_handle.vm) != EXCEPTION_NONE)
uloop_end();
Expand All @@ -1182,6 +1467,14 @@ void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
task_type = uc_type_declare(vm, "uloop.task", task_fns, close_task);
pipe_type = uc_type_declare(vm, "uloop.pipe", pipe_fns, close_pipe);

#ifdef HAVE_ULOOP_INTERVAL
interval_type = uc_type_declare(vm, "uloop.interval", interval_fns, close_interval);
#endif

#ifdef HAVE_ULOOP_SIGNAL
signal_type = uc_type_declare(vm, "uloop.signal", signal_fns, close_signal);
#endif

object_registry = ucv_array_new(vm);

uc_vm_registry_set(vm, "uloop.registry", object_registry);
Expand All @@ -1190,7 +1483,7 @@ void uc_module_init(uc_vm_t *vm, uc_value_t *scope)

if (signal_fd != -1 && uloop_init() == 0) {
signal_handle.vm = vm;
signal_handle.ufd.cb = uc_uloop_signal_cb;
signal_handle.ufd.cb = uc_uloop_vm_signal_cb;
signal_handle.ufd.fd = signal_fd;

uloop_fd_add(&signal_handle.ufd, ULOOP_READ);
Expand Down

0 comments on commit 0352a33

Please sign in to comment.