Run include-what-you-use.
[cubemap] / thread.cpp
index 03bfd9d..2aaf34f 100644 (file)
@@ -1,21 +1,33 @@
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <signal.h>
+#include <unistd.h>
 
+#include "log.h"
+#include "mutexlock.h"
 #include "thread.h"
 
+Thread::~Thread() {}
+
 void Thread::run()
 {
-       should_stop = false;
+       pthread_mutex_init(&should_stop_mutex, NULL);
+       should_stop_status = false;
        pthread_create(&worker_thread, NULL, &Thread::do_work_thunk, this);
 }
 
 void Thread::stop()
 {
-       should_stop = true;
-       pthread_kill(worker_thread, SIGHUP);
+       {
+               MutexLock lock(&should_stop_mutex);
+               should_stop_status = true;
+       }
+       wakeup();
        if (pthread_join(worker_thread, NULL) == -1) {
-               perror("pthread_join");
+               log_perror("pthread_join");
                exit(1);
        }
 }
@@ -23,7 +35,62 @@ void Thread::stop()
 void *Thread::do_work_thunk(void *arg)
 {
        Thread *thread = reinterpret_cast<Thread *>(arg);
+
+       // Block SIGHUP; only the main thread should see that.
+       // (This isn't strictly required, but it makes it easier to debug that indeed
+       // SIGUSR1 was what woke us up.)
+       sigset_t set;
+       sigaddset(&set, SIGHUP);
+       int err = pthread_sigmask(SIG_BLOCK, &set, NULL);
+       if (err != 0) {
+               errno = err;
+               log_perror("pthread_sigmask");
+               exit(1);
+       }
+
+       // Block SIGUSR1, and store the old signal mask.
+       sigemptyset(&set);
+       sigaddset(&set, SIGUSR1);
+       err = pthread_sigmask(SIG_BLOCK, &set, &thread->sigset_without_usr1_block);
+       if (err != 0) {
+               errno = err;
+               log_perror("pthread_sigmask");
+               exit(1);
+       }
+
+       // Call the right thunk.
        thread->do_work();
        return NULL;
 }
 
+bool Thread::wait_for_activity(int fd, short events, const struct timespec *timeout_ts)
+{
+       pollfd pfd;
+       pfd.fd = fd;
+       pfd.events = events;
+
+       for ( ;; ) {
+               int nfds = ppoll(&pfd, (fd == -1) ? 0 : 1, timeout_ts, &sigset_without_usr1_block);
+               if (nfds == -1 && errno == EINTR) {
+                       return false;
+               }
+               if (nfds == -1) {
+                       log_perror("poll");
+                       usleep(100000);
+                       continue;
+               }
+               assert(nfds <= 1);
+               return (nfds == 1);
+       }
+}
+
+void Thread::wakeup()
+{
+       pthread_kill(worker_thread, SIGUSR1);
+}
+
+bool Thread::should_stop()
+{
+       MutexLock lock(&should_stop_mutex);
+       return should_stop_status;
+}