* [libgpiod][PATCH] libgpiod: Allow to gracefully exit from a wait event
@ 2023-11-13 14:32 Adrien Zinger
2023-11-13 17:39 ` Erik Schilling
0 siblings, 1 reply; 4+ messages in thread
From: Adrien Zinger @ 2023-11-13 14:32 UTC (permalink / raw)
To: linux-gpio; +Cc: zinger.ad
Add a function in core `gpiod_line_request_wait_edge_events_or` with an
additional argument `fd` to trigger ppoll manually from another thread.
It allows users to gracefully cancel and join a worker thread waiting
for an edge event.
Signed-off-by: Adrien Zinger <zinger.ad@gmail.com>
---
include/gpiod.h | 17 ++++++++++++++
lib/internal.c | 38 ++++++++++++++++++++++++------
lib/internal.h | 1 +
lib/line-request.c | 9 +++++++
tests/tests-edge-event.c | 51 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 109 insertions(+), 7 deletions(-)
diff --git a/include/gpiod.h b/include/gpiod.h
index d86c6ac..cbc83f9 100644
--- a/include/gpiod.h
+++ b/include/gpiod.h
@@ -1176,6 +1176,23 @@ int gpiod_line_request_get_fd(struct gpiod_line_request *request);
int gpiod_line_request_wait_edge_events(struct gpiod_line_request *request,
int64_t timeout_ns);
+/**
+ * @brief Wait for edge events on any of the requested lines or a
+ * POLLHUP/POLLERR event on the given file descriptor.
+ * @param request GPIO line request.
+ * @param fd file descriptor from any I/O, channel, fifo, or pipe.
+ * @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function
+ * returns immediately. If set to a negative number, the
+ * function blocks indefinitely until an event becomes
+ * available.
+ * @return 0 if wait timed out, -1 if an error occurred, 1 if an event is
+ * pending, 2 if the file descriptor raised an event.
+ *
+ * Lines must have edge detection set for edge events to be emitted.
+ * By default edge detection is disabled.
+ */
+int gpiod_line_request_wait_edge_events_or(struct gpiod_line_request *request,
+ int fd, int64_t timeout_ns);
/**
* @brief Read a number of edge events from a line request.
* @param request GPIO line request.
diff --git a/lib/internal.c b/lib/internal.c
index c80d01f..aaf96ff 100644
--- a/lib/internal.c
+++ b/lib/internal.c
@@ -81,22 +81,18 @@ out:
return ret;
}
-int gpiod_poll_fd(int fd, int64_t timeout_ns)
+static int
+gpiod_poll_fds(struct pollfd *pfds, uint8_t len, int64_t timeout_ns)
{
struct timespec ts;
- struct pollfd pfd;
int ret;
- memset(&pfd, 0, sizeof(pfd));
- pfd.fd = fd;
- pfd.events = POLLIN | POLLPRI;
-
if (timeout_ns >= 0) {
ts.tv_sec = timeout_ns / 1000000000ULL;
ts.tv_nsec = timeout_ns % 1000000000ULL;
}
- ret = ppoll(&pfd, 1, timeout_ns < 0 ? NULL : &ts, NULL);
+ ret = ppoll(pfds, len, timeout_ns < 0 ? NULL : &ts, NULL);
if (ret < 0)
return -1;
else if (ret == 0)
@@ -105,6 +101,34 @@ int gpiod_poll_fd(int fd, int64_t timeout_ns)
return 1;
}
+int gpiod_poll_fd(int fd, int64_t timeout_ns)
+{
+ struct pollfd pfd;
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fd;
+ pfd.events = POLLIN | POLLPRI;
+
+ return gpiod_poll_fds(&pfd, 1, timeout_ns);
+}
+
+int gpiod_poll_fd_or(int fd1, int fd2, int64_t timeout_ns)
+{
+ struct pollfd pfds[2];
+ int ret;
+
+ memset(&pfds, 0, sizeof(pfds));
+ pfds[0].fd = fd1;
+ pfds[0].events = POLLIN | POLLPRI;
+ pfds[1].fd = fd2;
+ pfds[1].events = POLLIN | POLLERR;
+
+ ret = gpiod_poll_fds(pfds, 2, timeout_ns);
+ if (ret >= 1 && pfds[1].revents != 0)
+ return 2;
+ return ret;
+}
+
int gpiod_set_output_value(enum gpiod_line_value in, enum gpiod_line_value *out)
{
switch (in) {
diff --git a/lib/internal.h b/lib/internal.h
index 61d7aec..6b2105b 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -36,6 +36,7 @@ gpiod_info_event_from_uapi(struct gpio_v2_line_info_changed *uapi_evt);
struct gpiod_info_event *gpiod_info_event_read_fd(int fd);
int gpiod_poll_fd(int fd, int64_t timeout);
+int gpiod_poll_fd_or(int fd1, int fd2, int64_t timeout);
int gpiod_set_output_value(enum gpiod_line_value in,
enum gpiod_line_value *out);
diff --git a/lib/line-request.c b/lib/line-request.c
index e867d91..dd2a5a2 100644
--- a/lib/line-request.c
+++ b/lib/line-request.c
@@ -295,6 +295,15 @@ gpiod_line_request_wait_edge_events(struct gpiod_line_request *request,
return gpiod_poll_fd(request->fd, timeout_ns);
}
+GPIOD_API int
+gpiod_line_request_wait_edge_events_or(struct gpiod_line_request *request,
+ int fd, int64_t timeout_ns)
+{
+ assert(request);
+
+ return gpiod_poll_fd_or(request->fd, fd, timeout_ns);
+}
+
GPIOD_API int
gpiod_line_request_read_edge_events(struct gpiod_line_request *request,
struct gpiod_edge_event_buffer *buffer,
diff --git a/tests/tests-edge-event.c b/tests/tests-edge-event.c
index b744ca5..3281831 100644
--- a/tests/tests-edge-event.c
+++ b/tests/tests-edge-event.c
@@ -81,6 +81,17 @@ GPIOD_TEST_CASE(cannot_request_lines_in_output_mode_with_edge_detection)
gpiod_test_expect_errno(EINVAL);
}
+static gpointer trigger_cancel_channel(gpointer data)
+{
+ int cancel_sender = ((int *)data)[1];
+
+ g_usleep(1000);
+
+ g_assert_cmpint(write(cancel_sender, "\0", 1), ==, 1);
+
+ return NULL;
+}
+
static gpointer falling_and_rising_edge_events(gpointer data)
{
GPIOSimChip *sim = data;
@@ -361,6 +372,46 @@ GPIOD_TEST_CASE(read_rising_edge_event_polled)
g_thread_join(thread);
}
+GPIOD_TEST_CASE(cancel_edge_event_wait)
+{
+ static const guint offset = 2;
+ gint channel[2];
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(struct_gpiod_chip) chip = NULL;
+ g_autoptr(struct_gpiod_line_settings) settings = NULL;
+ g_autoptr(struct_gpiod_line_config) line_cfg = NULL;
+ g_autoptr(struct_gpiod_line_request) request = NULL;
+ g_autoptr(GThread) thread = NULL;
+ g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL;
+ gint ret;
+
+ chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim));
+ settings = gpiod_test_create_line_settings_or_fail();
+ line_cfg = gpiod_test_create_line_config_or_fail();
+ buffer = gpiod_test_create_edge_event_buffer_or_fail(64);
+
+ gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
+ gpiod_line_settings_set_edge_detection(settings,
+ GPIOD_LINE_EDGE_RISING);
+
+ gpiod_test_line_config_add_line_settings_or_fail(line_cfg, &offset, 1,
+ settings);
+
+ request = gpiod_test_chip_request_lines_or_fail(chip, NULL, line_cfg);
+
+ g_assert_cmpint(pipe(channel), ==, 0);
+
+ thread = g_thread_new("trigger-cancel-channel",
+ trigger_cancel_channel, channel);
+
+ ret = gpiod_line_request_wait_edge_events_or(request, channel[0], 1000000000);
+ g_assert_cmpint(ret, ==, 2); /* Canceled */
+ close(channel[0]);
+ close(channel[1]);
+ g_thread_join(thread);
+}
+
GPIOD_TEST_CASE(read_both_events_blocking)
{
/*
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [libgpiod][PATCH] libgpiod: Allow to gracefully exit from a wait event
2023-11-13 14:32 [libgpiod][PATCH] libgpiod: Allow to gracefully exit from a wait event Adrien Zinger
@ 2023-11-13 17:39 ` Erik Schilling
2023-11-14 10:09 ` Adrien Zinger
0 siblings, 1 reply; 4+ messages in thread
From: Erik Schilling @ 2023-11-13 17:39 UTC (permalink / raw)
To: Adrien Zinger, linux-gpio
On Mon Nov 13, 2023 at 3:32 PM CET, Adrien Zinger wrote:
> Add a function in core `gpiod_line_request_wait_edge_events_or` with an
> additional argument `fd` to trigger ppoll manually from another thread.
>
> It allows users to gracefully cancel and join a worker thread waiting
> for an edge event.
>
> Signed-off-by: Adrien Zinger <zinger.ad@gmail.com>
> ---
> include/gpiod.h | 17 ++++++++++++++
> lib/internal.c | 38 ++++++++++++++++++++++++------
> lib/internal.h | 1 +
> lib/line-request.c | 9 +++++++
> tests/tests-edge-event.c | 51 ++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 109 insertions(+), 7 deletions(-)
>
> diff --git a/include/gpiod.h b/include/gpiod.h
> index d86c6ac..cbc83f9 100644
> --- a/include/gpiod.h
> +++ b/include/gpiod.h
> @@ -1176,6 +1176,23 @@ int gpiod_line_request_get_fd(struct gpiod_line_request *request);
> int gpiod_line_request_wait_edge_events(struct gpiod_line_request *request,
> int64_t timeout_ns);
>
> +/**
> + * @brief Wait for edge events on any of the requested lines or a
> + * POLLHUP/POLLERR event on the given file descriptor.
> + * @param request GPIO line request.
> + * @param fd file descriptor from any I/O, channel, fifo, or pipe.
> + * @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function
> + * returns immediately. If set to a negative number, the
> + * function blocks indefinitely until an event becomes
> + * available.
> + * @return 0 if wait timed out, -1 if an error occurred, 1 if an event is
> + * pending, 2 if the file descriptor raised an event.
> + *
> + * Lines must have edge detection set for edge events to be emitted.
> + * By default edge detection is disabled.
> + */
> +int gpiod_line_request_wait_edge_events_or(struct gpiod_line_request *request,
> + int fd, int64_t timeout_ns);
This sounds like an oddly specific API... I wonder, why does
gpiod_line_request_get_fd [1] + doing the polling in your code not work
for you? Since you already got a file descriptor for the notification
that seems like little extra work. Or did you consider that but find it
too verbose?
[1] https://libgpiod.readthedocs.io/en/latest/group__line__request.html#ga5c0dbdcd8608b76e77b78bca9a6b03d7
- Erik
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [libgpiod][PATCH] libgpiod: Allow to gracefully exit from a wait event
2023-11-13 17:39 ` Erik Schilling
@ 2023-11-14 10:09 ` Adrien Zinger
2023-11-15 17:10 ` Bartosz Golaszewski
0 siblings, 1 reply; 4+ messages in thread
From: Adrien Zinger @ 2023-11-14 10:09 UTC (permalink / raw)
To: Erik Schilling, linux-gpio
> This sounds like an oddly specific API... I wonder, why does
> gpiod_line_request_get_fd [1] + doing the polling in your code
> not work
> for you? Since you already got a file descriptor for the
> notification that seems like little extra work. Or
> did you consider that but find
> it too verbose?
Until now it works like you said and it's not so verbose.
Actually I had the issue some weeks ago. And before digging inside the
library I have searched in the documentation and also asked on
stackoverflow [1] for a suggestion. Despite everything me and other
developers were not able to guess how to cancel a wait event from
another thread, without reading the code of the library. I thought
that it could be a good thing to provide that feature.
[1]
https://stackoverflow.com/questions/77222869/how-to-gracefully-cancel-gpiod-line-event-wait
Best regards,
Adrien
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [libgpiod][PATCH] libgpiod: Allow to gracefully exit from a wait event
2023-11-14 10:09 ` Adrien Zinger
@ 2023-11-15 17:10 ` Bartosz Golaszewski
0 siblings, 0 replies; 4+ messages in thread
From: Bartosz Golaszewski @ 2023-11-15 17:10 UTC (permalink / raw)
To: Adrien Zinger; +Cc: Erik Schilling, linux-gpio
On Tue, Nov 14, 2023 at 11:10 AM Adrien Zinger <zinger.ad@gmail.com> wrote:
>
> > This sounds like an oddly specific API... I wonder, why does
> > gpiod_line_request_get_fd [1] + doing the polling in your code
> > not work
> > for you? Since you already got a file descriptor for the
> > notification that seems like little extra work. Or
> > did you consider that but find
> > it too verbose?
>
> Until now it works like you said and it's not so verbose.
>
> Actually I had the issue some weeks ago. And before digging inside the
> library I have searched in the documentation and also asked on
> stackoverflow [1] for a suggestion. Despite everything me and other
> developers were not able to guess how to cancel a wait event from
> another thread, without reading the code of the library. I thought
> that it could be a good thing to provide that feature.
>
> [1]
> https://stackoverflow.com/questions/77222869/how-to-gracefully-cancel-gpiod-line-event-wait
>
> Best regards,
> Adrien
>
>
I agree with Erik, this looks like a very specific (obscure?) use-case
that's best implemented in-place using the fd getter. I try to keep
the library minimal and simple and this doesn't seem like a good fit
IMO.
Bart
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2023-11-15 17:10 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-11-13 14:32 [libgpiod][PATCH] libgpiod: Allow to gracefully exit from a wait event Adrien Zinger
2023-11-13 17:39 ` Erik Schilling
2023-11-14 10:09 ` Adrien Zinger
2023-11-15 17:10 ` Bartosz Golaszewski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).