From f52d11e86cd51acde2406a38f9049c0acf462164 Mon Sep 17 00:00:00 2001 From: Earnestly Date: Sat, 13 Jun 2015 23:56:51 +0100 Subject: [PATCH] Fix two corner cases which result in deadlocks. This patch attempts to fix two corner cases which can result in hhpc becoming deadlocked, . The first checks for the case where nanosleep() is interrupted which would set working to 0 and as hhpc never recovers from this case, the program will lockup indefinitely. Instead we resume where we left of if encountering EINTR. The second fix is a bit of a hack; on rare occasions the select() call in waitForMotion can seemingly block and practically never recover (I didn't wait for more than one hour). Instead I added a timeout to the select call, based on the gIdleTimeout value if it is greater than 1, otherwise it uses a 3 second delay. (This implies you can set a timeout of 2 seconds and have a 2 second timeout.) This doesn't really solve the issue of why select is getting stuck but does allow hhpc to recover and prevent it from holding the mouse hostage. --- hhpc.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/hhpc.c b/hhpc.c index f106f7f..8ed7196 100644 --- a/hhpc.c +++ b/hhpc.c @@ -38,6 +38,7 @@ #include #include #include +#include static int gIdleTimeout = 1; static int gVerbose = 0; @@ -90,8 +91,11 @@ static void delay(time_t sec, long msec) { sleep.tv_sec = sec; sleep.tv_nsec = (msec % 1000) * 1000 * 1000; - if (nanosleep(&sleep, NULL) == -1) { - signalHandler(0); + while (nanosleep(&sleep, &sleep) != 0) { + if (errno == EINTR) + continue; + else + signalHandler(0); } } @@ -158,6 +162,9 @@ static int grabPointer(Display *dpy, Window win, Cursor cursor, unsigned int mas } static void waitForMotion(Display *dpy, Window win, int timeout) { + struct timeval sleep; + sleep.tv_sec = (timeout <= 1) ? 3 : timeout; + int ready = 0; int xfd = ConnectionNumber(dpy); @@ -193,7 +200,13 @@ static void waitForMotion(Display *dpy, Window win, int timeout) { * is interruptible by signals, which allows ctrl+c to work. If we * were to just use XNextEvent() (which blocks), ctrl+c would not * work. */ - ready = select(xfd + 1, &fds, NULL, NULL, NULL); + if ((ready = select(xfd + 1, &fds, NULL, NULL, &sleep)) == -1) { + if (working) perror("hhpc: error while select()'ing"); + } + + if (ready == 0) { + if (gVerbose) fprintf(stderr, "hhpc: timeout\n"); + } if (ready > 0) { if (gVerbose) fprintf(stderr, "hhpc: event received, ungrabbing and sleeping\n"); @@ -211,12 +224,6 @@ static void waitForMotion(Display *dpy, Window win, int timeout) { delay(timeout, 0); } - else if (ready == 0) { - if (gVerbose) fprintf(stderr, "hhpc: timeout\n"); - } - else { - if (working) perror("hhpc: error while select()'ing"); - } } XUngrabPointer(dpy, CurrentTime);