Signal thread stop through a pipe; fixes issues where the statistics thread would...
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 14 Apr 2013 17:53:48 +0000 (19:53 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 14 Apr 2013 17:53:48 +0000 (19:53 +0200)
stats.cpp
thread.cpp
thread.h

index c9af688..f5916ed 100644 (file)
--- a/stats.cpp
+++ b/stats.cpp
@@ -5,6 +5,8 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <sys/poll.h>
+#include <errno.h>
 #include <vector>
 
 #include "client.h"
@@ -71,9 +73,25 @@ void StatsThread::do_work()
                }
 
 sleep:
-               int left_to_sleep = stats_interval;
-               do {
-                       left_to_sleep = sleep(left_to_sleep);
-               } while (left_to_sleep > 0 && !should_stop);
+               // Wait until the stop_fd pipe is closed, stats_interval timeout,
+               // or a spurious signal. (The latter will cause us to write stats
+               // too often, but that's okay.)
+               pollfd pfd;
+               pfd.fd = stop_fd_read;
+               pfd.events = POLLIN | POLLRDHUP;
+
+               int nfds = poll(&pfd, 1, stats_interval * 1000);
+               if (nfds == 0 || (nfds == -1 && errno == EINTR)) {
+                       continue;
+               }
+               if (nfds == 1) {
+                       // Should stop.
+                       break;
+               }
+               if (nfds == -1) {
+                       perror("poll");
+                       usleep(100000);
+                       continue;
+               }
        }
 }
index 7aded3b..b263be6 100644 (file)
@@ -1,6 +1,9 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include <signal.h>
+#include <errno.h>
 
 #include "thread.h"
        
@@ -9,17 +12,53 @@ Thread::~Thread() {}
 void Thread::run()
 {
        should_stop = false;
+       int pipefd[2];
+       if (pipe2(pipefd, O_CLOEXEC) == -1) {
+               perror("pipe");
+               exit(1);
+       }
+       stop_fd_read = pipefd[0];
+       stop_fd_write = pipefd[1];
        pthread_create(&worker_thread, NULL, &Thread::do_work_thunk, this);
 }
 
 void Thread::stop()
 {
        should_stop = true;
+       char ch = 0;
+       int err;
+       do {
+               err = write(stop_fd_write, &ch, 1);
+       } while (err == -1 && errno == EINTR);
+
+       if (err == -1) {
+               perror("write");
+               exit(1);
+       }
+
+       do {
+               err = close(stop_fd_write);
+       } while (err == -1 && errno == EINTR);
+
+       if (err == -1) {
+               perror("close");
+               // Can continue (we have close-on-exec).
+       }
+
        pthread_kill(worker_thread, SIGHUP);
        if (pthread_join(worker_thread, NULL) == -1) {
                perror("pthread_join");
                exit(1);
        }
+       
+       do {
+               err = close(stop_fd_read);
+       } while (err == -1 && errno == EINTR);
+
+       if (err == -1) {
+               perror("close");
+               // Can continue (we have close-on-exec).
+       }
 }
 
 void *Thread::do_work_thunk(void *arg)
index 9c9bdc6..26e648e 100644 (file)
--- a/thread.h
+++ b/thread.h
@@ -22,8 +22,14 @@ protected:
 
        volatile bool should_stop;
 
+       // A pipe that you can poll on if you want to see when should_stop
+       // has been set to true; stop() will write a single byte to the pipe
+       // and then close the other end.
+       int stop_fd_read;
+
 private:
        pthread_t worker_thread;
+       int stop_fd_write;
 };
 
 #endif  // !defined(_THREAD_H)