Signal with different u_ctx if current_uthread set
[akaros.git] / user / pthread / pthread.c
index ed99a64..192dfb8 100644 (file)
@@ -1,3 +1,6 @@
+// Needed for sigmask functions...
+#define _GNU_SOURCE
+
 #include <ros/trapframe.h>
 #include <pthread.h>
 #include <vcore.h>
@@ -45,7 +48,8 @@ void pth_preempt_pending(void);
 void pth_spawn_thread(uintptr_t pc_start, void *data);
 
 /* Event Handlers */
-static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type);
+static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
+                               void *data);
 
 struct schedule_ops pthread_sched_ops = {
        pth_sched_entry,
@@ -65,6 +69,40 @@ 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();
+       struct user_context *ctx;
+       if (current_uthread) {
+               struct preempt_data *vcpd = vcpd_of(vcoreid);
+        ctx = &vcpd->uthread_ctx;
+       } else {
+               ctx = &thread->uthread.u_ctx;
+       }
+
+       void *temp_tls_desc = get_tls_desc(vcoreid);
+       set_tls_desc(thread->uthread.tls_desc, vcoreid);
+       trigger_posix_signal(signo, info, ctx);
+       set_tls_desc(temp_tls_desc, vcoreid);
+}
+
+static void __pthread_trigger_pending_posix_signals(pthread_t thread)
+{
+       if (thread->sigpending) {
+               sigset_t andset = thread->sigpending & (~thread->sigmask);
+               if (!__sigisemptyset(&andset)) {
+                       for (int i = 1; i < _NSIG; i++) {
+                               if (__sigismember(&andset, i)) {
+                                       __sigdelset(&thread->sigpending, i);
+                                       __pthread_trigger_posix_signal(thread, i, NULL);
+                               }
+                       }
+               }
+       }
+}
+
 /* 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). */
@@ -72,7 +110,10 @@ void __attribute__((noreturn)) pth_sched_entry(void)
 {
        uint32_t vcoreid = vcore_id();
        if (current_uthread) {
+               /* Run any pending posix signal handlers registered via pthread_kill */
+               __pthread_trigger_pending_posix_signals((pthread_t)current_uthread);
                run_current_uthread();
+               /* Run the thread itself */
                assert(0);
        }
        /* no one currently running, so lets get someone from the ready queue */
@@ -108,6 +149,9 @@ 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 */
+       __pthread_trigger_pending_posix_signals(new_thread);
+       /* Run the thread itself */
        run_uthread((struct uthread*)new_thread);
        assert(0);
 }
@@ -198,7 +242,8 @@ static void restart_thread(struct syscall *sysc)
 
 /* This handler is usually run in vcore context, though I can imagine it being
  * called by a uthread in some other threading library. */
-static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type)
+static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
+                               void *data)
 {
        struct syscall *sysc;
        assert(in_vcore_context());
@@ -275,15 +320,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 +443,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);
@@ -416,7 +460,7 @@ void pthread_lib_init(void)
        enable_kevent(EV_USER_IPI, 0, EVENT_IPI | EVENT_VCORE_PRIVATE);
 
        /* Handle syscall events. */
-       ev_handlers[EV_SYSCALL] = pth_handle_syscall;
+       register_ev_handler(EV_SYSCALL, pth_handle_syscall, 0);
        /* Set up the per-vcore structs to track outstanding syscalls */
        sysc_mgmt = malloc(sizeof(struct sysc_mgmt) * max_vcores());
        assert(sysc_mgmt);
@@ -466,8 +510,8 @@ void pthread_lib_init(void)
        atomic_init(&threads_total, 1);                 /* one for thread0 */
 }
 
-int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
-                   void *(*start_routine)(void *), void *arg)
+int __pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+                     void *(*start_routine)(void *), void *arg)
 {
        struct uth_thread_attr uth_attr = {0};
        run_once(pthread_lib_init());
@@ -482,6 +526,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 */
@@ -503,12 +549,19 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        if (need_tls)
                uth_attr.want_tls = TRUE;
        uthread_init((struct uthread*)pthread, &uth_attr);
-       pth_thread_runnable((struct uthread*)pthread);
        *thread = pthread;
        atomic_inc(&threads_total);
        return 0;
 }
 
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+                   void *(*start_routine)(void *), void *arg)
+{
+       if (!__pthread_create(thread, attr, start_routine, arg))
+               pth_thread_runnable((struct uthread*)*thread);
+       return 0;
+}
+
 /* Helper that all pthread-controlled yield paths call.  Just does some
  * accounting.  This is another example of how the much-loathed (and loved)
  * active queue is keeping us honest.  Need to export for sem and friends. */
@@ -1019,17 +1072,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)