Skip to contentSkip to conversation

treewide: allow polling functions to block indefinitely

PATCH In review Bartosz Golaszewski 3 participants 4 messages Jul 1, 2022

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 treewide: allow polling functions to block indefinitely

All polling system calls have some way of being instructed to block indefinitely until some event is registered on the file descriptor.

Make both the gpiod_chip_wait_info_event() and gpiod_line_request_wait_edge_event() accept negative timeout values in which case the underlying ppoll() will block indefinitely.

---
 include/gpiod.h    | 14 ++++++++++----
 lib/chip.c         |  2 +-
 lib/internal.c     | 10 ++++++----
 lib/internal.h     |  2 +-
 lib/line-request.c |  2 +-
 5 files changed, 19 insertions(+), 11 deletions(-)
include/gpiod.h +10 -4
index 5595ff2..d8b5f39 100644
@@ -135,11 +135,14 @@ int gpiod_chip_get_fd(struct gpiod_chip *chip);
  * @brief Wait for line status change events on any of the watched lines
  *	  on the chip.
  * @param chip GPIO chip object.
- * @param timeout_ns Wait time limit in nanoseconds.
+ * @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function
+ *		     returns immediatelly. 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.
  */
-int gpiod_chip_wait_info_event(struct gpiod_chip *chip, uint64_t timeout_ns);
+int gpiod_chip_wait_info_event(struct gpiod_chip *chip, int64_t timeout_ns);
 
 /**
  * @brief Read a single line status change event from the chip.
@@ -1320,7 +1323,10 @@ int gpiod_line_request_get_fd(struct gpiod_line_request *request);
 /**
  * @brief Wait for edge events on any of the requested lines.
  * @param request GPIO line request.
- * @param timeout_ns Wait time limit in nanoseconds.
+ * @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function
+ *		     returns immediatelly. 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.
  *q
@@ -1328,7 +1334,7 @@ int gpiod_line_request_get_fd(struct gpiod_line_request *request);
  * By default edge detection is disabled.
  */
 int gpiod_line_request_wait_edge_event(struct gpiod_line_request *request,
-				       uint64_t timeout_ns);
+				       int64_t timeout_ns);
 
 /**
  * @brief Read a number of edge events from a line request.
lib/chip.c +1 -1
index fc3dda2..038d3dd 100644
@@ -145,7 +145,7 @@ GPIOD_API int gpiod_chip_get_fd(struct gpiod_chip *chip)
 }
 
 GPIOD_API int gpiod_chip_wait_info_event(struct gpiod_chip *chip,
-					 uint64_t timeout_ns)
+					 int64_t timeout_ns)
 {
 	return gpiod_poll_fd(chip->fd, timeout_ns);
 }
lib/internal.c +6 -4
index b7da67e..d948814 100644
@@ -7,7 +7,7 @@
 
 #include "internal.h"
 
-int gpiod_poll_fd(int fd, uint64_t timeout_ns)
+int gpiod_poll_fd(int fd, int64_t timeout_ns)
 {
 	struct timespec ts;
 	struct pollfd pfd;
@@ -17,10 +17,12 @@ int gpiod_poll_fd(int fd, uint64_t timeout_ns)
 	pfd.fd = fd;
 	pfd.events = POLLIN | POLLPRI;
 
-	ts.tv_sec = timeout_ns / 1000000000ULL;
-	ts.tv_nsec = timeout_ns % 1000000000ULL;
+	if (timeout_ns >= 0) {
+		ts.tv_sec = timeout_ns / 1000000000ULL;
+		ts.tv_nsec = timeout_ns % 1000000000ULL;
+	}
 
-	ret = ppoll(&pfd, 1, &ts, NULL);
+	ret = ppoll(&pfd, 1, timeout_ns < 0 ? NULL : &ts, NULL);
 	if (ret < 0)
 		return -1;
 	else if (ret == 0)
lib/internal.h +1 -1
index c87df91..fab12c3 100644
@@ -36,7 +36,7 @@ struct gpiod_info_event *
 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, uint64_t timeout);
+int gpiod_poll_fd(int fd, int64_t timeout);
 
 void gpiod_line_mask_zero(uint64_t *mask);
 void gpiod_line_mask_fill(uint64_t *mask);
lib/line-request.c +1 -1
index 33f7f67..04bd78d 100644
@@ -200,7 +200,7 @@ GPIOD_API int gpiod_line_request_get_fd(struct gpiod_line_request *request)
 
 GPIOD_API int
 gpiod_line_request_wait_edge_event(struct gpiod_line_request *request,
-				   uint64_t timeout_ns)
+				   int64_t timeout_ns)
 {
 	return gpiod_poll_fd(request->fd, timeout_ns);
 }
Signature · 1 line
2.34.1
#1 lore raw
Quoted 7 lines · Bartosz Golaszewski

All polling system calls have some way of being instructed to block indefinitely until some event is registered on the file descriptor.

Make both the gpiod_chip_wait_info_event() and gpiod_line_request_wait_edge_event() accept negative timeout values in which case the underlying ppoll() will block indefinitely.

Long time no user space done by me, so here my silly question: how to kill a task that is blocking indefinitely in ppoll()?

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#2 lore raw
Quoted 5 lines · Andy Shevchenko
Quoted 4 lines · Bartosz Golaszewski

All polling system calls have some way of being instructed to block indefinitely until some event is registered on the file descriptor.

Quoted 7 lines
Quoted 3 lines

Make both the gpiod_chip_wait_info_event() and gpiod_line_request_wait_edge_event() accept negative timeout values in which case the underlying ppoll() will block indefinitely.

Long time no user space done by me, so here my silly question: how to kill a task that is blocking indefinitely in ppoll()?

Yeah, you don't. In a multi-threaded app you never need to - that thread is dedicated to handling events, and it will keep doing that until the app exits.

But if you want to be able to cancel the wait then you add an eventFd, or similar, that you signal to wake the thread to exit. And that is why libgpiod exposes the fd so the user can build their own ppoll should they want to do that.

Cheers,
Kent.

#3 lore raw
In reply to Andy Shevchenko
Quoted 6 lines · Andy Shevchenko
Quoted 4 lines · Bartosz Golaszewski

All polling system calls have some way of being instructed to block indefinitely until some event is registered on the file descriptor.

Quoted 7 lines
Quoted 3 lines

Make both the gpiod_chip_wait_info_event() and gpiod_line_request_wait_edge_event() accept negative timeout values in which case the underlying ppoll() will block indefinitely.

Long time no user space done by me, so here my silly question: how to kill a task that is blocking indefinitely in ppoll()?

Send a signal to it. It will return -1 and set errno to EINTR. Unless you explicitly masked all signals. In that case kill -9 still works. :)

Bart