Implement pthead_kill() and sigmask stuff for pthreads
authorKevin Klues <klueska@cs.berkeley.edu>
Tue, 27 May 2014 23:24:49 +0000 (16:24 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Tue, 27 May 2014 23:25:25 +0000 (16:25 -0700)
user/parlib/signal.c
user/pthread/pthread.c
user/pthread/pthread.h
user/utest/signal.c [new file with mode: 0644]

index 3dac5fa..60ead50 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdio.h>
 
 #include <event.h>
+#include <errno.h>
 #include <assert.h>
 #include <ros/procinfo.h>
 #include <ros/syscall.h>
@@ -117,6 +118,10 @@ void trigger_posix_signal(int sig_nr, struct siginfo *info, void *aux)
        }
 
        if (action->sa_flags & SA_SIGINFO) {
+               /* If NULL info struct passed in, construct our own */
+               struct siginfo s = {0};
+               if (info == NULL)
+                       info = &s;
                /* Make sure the caller either already set singo in the info struct, or
                 * if they didn't, make sure it has been zeroed out (i.e. not just some
                 * garbage on the stack. */
@@ -157,19 +162,33 @@ void init_posix_signals(void)
        register_kevent_q(posix_sig_ev_q, EV_POSIX_SIGNAL);
 }
 
-/* Will need to do these if we have signal masks (sigprocmask style) */
 int sigaddset(sigset_t *__set, int __signo)
 {
+       if (__signo == 0 || __signo > _NSIG) {
+               errno = EINVAL;
+               return -1;
+       }
+       __sigaddset(__set, __signo);
        return 0;
 }
 
 int sigdelset(sigset_t *__set, int __signo)
 {
+       if (__signo == 0 || __signo > _NSIG) {
+               errno = EINVAL;
+               return -1;
+       }
+       __sigdelset(__set, __signo);
        return 0;
 }
 
 int sigismember(__const sigset_t *__set, int __signo)
 {
+       if (__signo == 0 || __signo > _NSIG) {
+               errno = EINVAL;
+               return -1;
+       }
+       __sigismember(__set, __signo);
        return 0;
 }
 
@@ -179,6 +198,8 @@ int sigismember(__const sigset_t *__set, int __signo)
 int sigprocmask(int __how, __const sigset_t *__restrict __set,
                 sigset_t *__restrict __oset)
 {
+       printf("Function not supported generically! "
+           "Use 2LS specific function e.g. pthread_sigmask\n");
        return 0;
 }
 
index 8bd56ec..2cd0e16 100644 (file)
@@ -1,3 +1,6 @@
+// Needed for sigmask functions...
+#define _GNU_SOURCE
+
 #include <ros/trapframe.h>
 #include <pthread.h>
 #include <vcore.h>
@@ -65,6 +68,18 @@ struct schedule_ops *sched_ops = &pthread_sched_ops;
 static void __pthread_free_stack(struct pthread_tcb *pt);
 static int __pthread_allocate_stack(struct pthread_tcb *pt);
 
+/* Trigger a posix signal on a pthread from vcore context */
+static void __pthread_trigger_posix_signal(pthread_t thread, int signo,
+                                           struct siginfo *info)
+{
+       int vcoreid = vcore_id();
+       void *temp_tls_desc = get_tls_desc(vcoreid);
+       struct uthread *uthread = (struct uthread*)thread;
+       set_tls_desc(uthread->tls_desc, vcore_id());
+       trigger_posix_signal(signo, info, &uthread->u_ctx);
+       set_tls_desc(temp_tls_desc, vcoreid);
+}
+
 /* Called from vcore entry.  Options usually include restarting whoever was
  * running there before or running a new thread.  Events are handled out of
  * event.c (table of function pointers, stuff like that). */
@@ -108,6 +123,19 @@ void __attribute__((noreturn)) pth_sched_entry(void)
                        vcore_yield(FALSE);
        } while (1);
        assert(new_thread->state == PTH_RUNNABLE);
+       /* Run any pending posix signal handlers registered via pthread_kill */
+       if (new_thread->sigpending) {
+               sigset_t andset = new_thread->sigpending & (~new_thread->sigmask);
+               if (!__sigisemptyset(&andset)) {
+                       for (int i = 1; i < _NSIG; i++) {
+                               if (__sigismember(&andset, i)) {
+                                       __sigdelset(&new_thread->sigpending, i);
+                                       __pthread_trigger_posix_signal(new_thread, i, NULL);
+                               }
+                       }
+               }
+       }
+       /* Run the thread itself */
        run_uthread((struct uthread*)new_thread);
        assert(0);
 }
@@ -275,15 +303,12 @@ void pth_thread_refl_fault(struct uthread *uthread, unsigned int trap_nr,
        }
 
        if (!(err & PF_VMR_BACKED)) {
-               struct siginfo info = {0};
-               info.si_code = SEGV_MAPERR;
-               info.si_addr = (void*)aux;
-
-               int vcoreid = vcore_id();
-               void *temp_tls_desc = get_tls_desc(vcoreid);
-               set_tls_desc(uthread->tls_desc, vcore_id());
-               trigger_posix_signal(SIGSEGV, &info, &uthread->u_ctx);
-               set_tls_desc(temp_tls_desc, vcoreid);
+               if (!__sigismember(&pthread->sigmask, SIGSEGV)) {
+                       struct siginfo info = {0};
+                       info.si_code = SEGV_MAPERR;
+                       info.si_addr = (void*)aux;
+                       __pthread_trigger_posix_signal(pthread, SIGSEGV, &info);
+               }
                pth_thread_runnable(uthread);
                return;
        }
@@ -401,6 +426,8 @@ void pthread_lib_init(void)
        t->detached = TRUE;
        t->state = PTH_RUNNING;
        t->joiner = 0;
+       __sigemptyset(&t->sigmask);
+       __sigemptyset(&t->sigpending);
        assert(t->id == 0);
        /* Put the new pthread (thread0) on the active queue */
        mcs_pdr_lock(&queue_lock);
@@ -482,6 +509,8 @@ int __pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        pthread->id = get_next_pid();
        pthread->detached = FALSE;                              /* default */
        pthread->joiner = 0;
+       pthread->sigmask = ((pthread_t)current_uthread)->sigmask;
+       __sigemptyset(&pthread->sigpending);
        /* Respect the attributes */
        if (attr) {
                if (attr->stacksize)                                    /* don't set a 0 stacksize */
@@ -1026,17 +1055,38 @@ int pthread_detach(pthread_t thread)
        return 0;
 }
 
-int pthread_kill (pthread_t __threadid, int __signo)
+int pthread_kill(pthread_t thread, int signo)
 {
-       printf("pthread_kill is not yet implemented!");
-       return -1;
+       // Slightly racy with clearing of mask when triggering the signal, but
+       // that's OK, as signals are inherently racy since they don't queue up.
+       return sigaddset(&thread->sigpending, signo);
 }
 
 
 int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
 {
-       printf("pthread_sigmask is not yet implemented!");
-       return -1;
+       if (how != SIG_BLOCK && how != SIG_SETMASK && how != SIG_UNBLOCK) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       pthread_t pthread = ((struct pthread_tcb*)current_uthread);
+       if (oset)
+               *oset = pthread->sigmask;
+       switch (how) {
+               case SIG_BLOCK:
+                       pthread->sigmask = pthread->sigmask | *set;
+                       break;
+               case SIG_SETMASK:
+                       pthread->sigmask = *set;
+                       break;
+               case SIG_UNBLOCK:
+                       pthread->sigmask = pthread->sigmask & ~(*set);
+                       break;
+       }
+       // Ensures any signals we just unmasked get processed if they are pending
+       pthread_yield();
+       return 0;
 }
 
 int pthread_sigqueue(pthread_t *thread, int sig, const union sigval value)
index 8b4d765..89a88b7 100644 (file)
@@ -39,7 +39,8 @@ struct pthread_tcb {
        void *(*start_routine)(void*);
        void *arg;
        void *retval;
-       uint64_t sigmask;
+       sigset_t sigmask;
+       sigset_t sigpending;
 };
 typedef struct pthread_tcb* pthread_t;
 TAILQ_HEAD(pthread_queue, pthread_tcb);
diff --git a/user/utest/signal.c b/user/utest/signal.c
new file mode 100644 (file)
index 0000000..bf732d2
--- /dev/null
@@ -0,0 +1,58 @@
+#include <utest.h>
+#include <pthread.h>
+
+TEST_SUITE("SIGNALS");
+
+/* <--- Begin definition of test cases ---> */
+
+bool test_sigmask(void) {
+       int count = 0;
+       pthread_t sigphandle;
+       void *thread_handler(void *arg)
+       {
+               sigset_t s;
+               sigemptyset(&s);
+               sigaddset(&s, SIGUSR2);
+               pthread_sigmask(SIG_BLOCK, &s, NULL);
+               for (int i=0; i<1000; i++)
+                       pthread_yield();
+       }
+       void signal_handler(int signo)
+       {
+               sigphandle = pthread_self();
+               __sync_fetch_and_add(&count, 1);
+       }
+
+       struct sigaction sigact = {.sa_handler = signal_handler, 0};
+       sigaction(SIGUSR1, &sigact, 0);
+       sigaction(SIGUSR2, &sigact, 0);
+
+       pthread_t phandle;
+       pthread_create(&phandle, NULL, thread_handler, NULL);
+       for (int i=0; i<100; i++)
+               pthread_yield();
+       pthread_kill(phandle, SIGUSR1);
+       pthread_kill(phandle, SIGUSR2);
+       pthread_join(phandle, NULL);
+
+       printf("count: %d\n", count);
+       UT_ASSERT_M("Should only receive one signal", count == 1); 
+       UT_ASSERT_M("Signal handler run on wrong thread", sigphandle == phandle); 
+       return true;
+}
+
+/* <--- End definition of test cases ---> */
+
+struct utest utests[] = {
+       UTEST_REG(sigmask),
+};
+int num_utests = sizeof(utests) / sizeof(struct utest);
+
+int main(int argc, char *argv[]) {
+       // Run test suite passing it all the args as whitelist of what tests to run.
+       char **whitelist = &argv[1];
+       int whitelist_len = argc - 1;
+       RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len);
+}
+
+