]> git.sesse.net Git - cubemap/blobdiff - thread.cpp
Tweak the MutexLock implementation slightly, so as to confuse Coverity less.
[cubemap] / thread.cpp
index f719eacb95c5379a2d664f23c49c04e0b50b767b..72bc320b0b32359dcbbe51352b2df8ca77850a45 100644 (file)
@@ -1,71 +1,97 @@
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <errno.h>
 
 #include "log.h"
+#include "mutexlock.h"
 #include "thread.h"
-       
+
 Thread::~Thread() {}
 
 void Thread::run()
 {
-       should_stop = false;
-       int pipefd[2];
-       if (pipe2(pipefd, O_CLOEXEC) == -1) {
-               log_perror("pipe");
-               exit(1);
-       }
-       stop_fd_read = pipefd[0];
-       stop_fd_write = pipefd[1];
+       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;
-       char ch = 0;
-       int err;
-       do {
-               err = write(stop_fd_write, &ch, 1);
-       } while (err == -1 && errno == EINTR);
-
-       if (err == -1) {
-               log_perror("write");
+       {
+               MutexLock lock(&should_stop_mutex);
+               should_stop_status = true;
+       }
+       wakeup();
+       if (pthread_join(worker_thread, NULL) == -1) {
+               log_perror("pthread_join");
                exit(1);
        }
+}
 
-       do {
-               err = close(stop_fd_write);
-       } while (err == -1 && errno == EINTR);
+void *Thread::do_work_thunk(void *arg)
+{
+       Thread *thread = reinterpret_cast<Thread *>(arg);
 
-       if (err == -1) {
-               log_perror("close");
-               // Can continue (we have close-on-exec).
+       // 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;
+       sigemptyset(&set);
+       sigaddset(&set, SIGHUP);
+       int err = pthread_sigmask(SIG_BLOCK, &set, NULL);
+       if (err != 0) {
+               errno = err;
+               log_perror("pthread_sigmask");
+               exit(1);
        }
 
-       pthread_kill(worker_thread, SIGHUP);
-       if (pthread_join(worker_thread, NULL) == -1) {
-               log_perror("pthread_join");
+       // 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);
        }
-       
-       do {
-               err = close(stop_fd_read);
-       } while (err == -1 && errno == EINTR);
 
-       if (err == -1) {
-               log_perror("close");
-               // Can continue (we have close-on-exec).
+       // 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::do_work_thunk(void *arg)
+void Thread::wakeup()
 {
-       Thread *thread = reinterpret_cast<Thread *>(arg);
-       thread->do_work();
-       return NULL;
+       pthread_kill(worker_thread, SIGUSR1);
 }
 
+bool Thread::should_stop()
+{
+       MutexLock lock(&should_stop_mutex);
+       return should_stop_status;
+}