[libgpiod][PATCH] libgpiod: Allow to gracefully exit from a wait event

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxx>
---
 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





[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux