diff mbox

[2/2] amidi: fix timeout handling

Message ID 570AA9D5.4010708@ladisch.de (mailing list archive)
State New, archived
Headers show

Commit Message

Clemens Ladisch April 10, 2016, 7:30 p.m. UTC
The timeout is not supposed to expire when ignored messages are
received.  This cannot be handled with the poll() timeout, so add
a separate timer.

Reported-by: Martin Tarenskeen <m.tarenskeen@zonnet.nl>
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
 amidi/amidi.c |   69 ++++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 53 insertions(+), 16 deletions(-)
diff mbox

Patch

diff --git a/amidi/amidi.c b/amidi/amidi.c
index 4978249..a3515b1 100644
--- a/amidi/amidi.c
+++ b/amidi/amidi.c
@@ -25,9 +25,11 @@ 
 #include <stdarg.h>
 #include <string.h>
 #include <ctype.h>
+#include <math.h>
 #include <getopt.h>
 #include <errno.h>
 #include <signal.h>
+#include <sys/timerfd.h>
 #include <sys/types.h>
 #include <sys/poll.h>
 #include <sys/stat.h>
@@ -37,6 +39,8 @@ 
 #include "aconfig.h"
 #include "version.h"

+#define NSEC_PER_SEC 1000000000L
+
 static int do_device_list, do_rawmidi_list;
 static char *port_name = "default";
 static char *send_file_name;
@@ -46,7 +50,7 @@  static char *send_data;
 static int send_data_length;
 static int receive_file;
 static int dump;
-static int timeout;
+static float timeout;
 static int stop;
 static snd_rawmidi_t *input, **inputp;
 static snd_rawmidi_t *output, **outputp;
@@ -425,6 +429,7 @@  int main(int argc, char *argv[])
 	int c, err, ok = 0;
 	int ignore_system_realtime = 1;
 	int do_send_hex = 0;
+	struct itimerspec itimerspec = { .it_interval = { 0, 0 } };

 	while ((c = getopt_long(argc, argv, short_options,
 		     		long_options, NULL)) != -1) {
@@ -459,7 +464,7 @@  int main(int argc, char *argv[])
 			dump = 1;
 			break;
 		case 't':
-			timeout = atoi(optarg);
+			timeout = atof(optarg);
 			break;
 		case 'a':
 			ignore_system_realtime = 0;
@@ -547,40 +552,64 @@  int main(int argc, char *argv[])

 	if (inputp) {
 		int read = 0;
-		int npfds, time = 0;
+		int npfds;
 		struct pollfd *pfds;

-		timeout *= 1000;
-		npfds = snd_rawmidi_poll_descriptors_count(input);
+		npfds = 1 + snd_rawmidi_poll_descriptors_count(input);
 		pfds = alloca(npfds * sizeof(struct pollfd));
-		snd_rawmidi_poll_descriptors(input, pfds, npfds);
+
+		if (timeout > 0) {
+			pfds[0].fd = timerfd_create(CLOCK_MONOTONIC, 0);
+			if (pfds[0].fd == -1) {
+				error("cannot create timer: %s", strerror(errno));
+				goto _exit;
+			}
+			pfds[0].events = POLLIN;
+		} else {
+			pfds[0].fd = -1;
+		}
+
+		snd_rawmidi_poll_descriptors(input, &pfds[1], npfds - 1);
+
 		signal(SIGINT, sig_handler);
+
+		if (timeout > 0) {
+			float timeout_int;
+
+			itimerspec.it_value.tv_nsec = modff(timeout, &timeout_int) * NSEC_PER_SEC;
+			itimerspec.it_value.tv_sec = timeout_int;
+			err = timerfd_settime(pfds[0].fd, 0, &itimerspec, NULL);
+			if (err < 0) {
+				error("cannot set timer: %s", strerror(errno));
+				goto _exit;
+			}
+		}
 		for (;;) {
 			unsigned char buf[256];
 			int i, length;
 			unsigned short revents;

-			err = poll(pfds, npfds, 200);
+			err = poll(pfds, npfds, -1);
 			if (stop || (err < 0 && errno == EINTR))
 				break;
 			if (err < 0) {
 				error("poll failed: %s", strerror(errno));
 				break;
 			}
-			if (err == 0) {
-				time += 200;
-				if (timeout && time >= timeout)
-					break;
-				continue;
-			}
-			if ((err = snd_rawmidi_poll_descriptors_revents(input, pfds, npfds, &revents)) < 0) {
+
+			err = snd_rawmidi_poll_descriptors_revents(input, &pfds[1], npfds - 1, &revents);
+			if (err < 0) {
 				error("cannot get poll events: %s", snd_strerror(errno));
 				break;
 			}
 			if (revents & (POLLERR | POLLHUP))
 				break;
-			if (!(revents & POLLIN))
+			if (!(revents & POLLIN)) {
+				if (pfds[0].revents & POLLIN)
+					break;
 				continue;
+			}
+
 			err = snd_rawmidi_read(input, buf, sizeof(buf));
 			if (err == -EAGAIN)
 				continue;
@@ -595,7 +624,7 @@  int main(int argc, char *argv[])
 			if (length == 0)
 				continue;
 			read += length;
-			time = 0;
+
 			if (receive_file != -1)
 				write(receive_file, buf, length);
 			if (dump) {
@@ -603,6 +632,14 @@  int main(int argc, char *argv[])
 					print_byte(buf[i]);
 				fflush(stdout);
 			}
+
+			if (timeout > 0) {
+				err = timerfd_settime(pfds[0].fd, 0, &itimerspec, NULL);
+				if (err < 0) {
+					error("cannot set timer: %s", strerror(errno));
+					break;
+				}
+			}
 		}
 		if (isatty(fileno(stdout)))
 			printf("\n%d bytes read\n", read);