Implement poll() on top of select()
[akaros.git] / user / pthread / pthread.c
index b52554c..9a6691e 100644 (file)
@@ -1,13 +1,10 @@
-// Needed for sigmask functions...
-#define _GNU_SOURCE
-
 #include <ros/trapframe.h>
 #include "pthread.h"
 #include <parlib/vcore.h>
 #include <parlib/mcs.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
+#include <parlib/assert.h>
 #include <stdio.h>
 #include <errno.h>
 #include <parlib/parlib.h>
@@ -18,6 +15,8 @@
 #include <sys/mman.h>
 #include <parlib/event.h>
 #include <parlib/ucq.h>
+#include <parlib/signal.h>
+#include <parlib/arch/trap.h>
 
 struct pthread_queue ready_queue = TAILQ_HEAD_INITIALIZER(ready_queue);
 struct pthread_queue active_queue = TAILQ_HEAD_INITIALIZER(active_queue);
@@ -25,7 +24,6 @@ struct mcs_pdr_lock queue_lock;
 int threads_ready = 0;
 int threads_active = 0;
 atomic_t threads_total;
-bool can_adjust_vcores = TRUE;
 bool need_tls = TRUE;
 
 /* Array of per-vcore structs to manage waiting on syscalls and handling
@@ -35,6 +33,7 @@ struct sysc_mgmt *sysc_mgmt = 0;
 /* Helper / local functions */
 static int get_next_pid(void);
 static inline void spin_to_sleep(unsigned int spins, unsigned int *spun);
+static inline void pthread_exit_no_cleanup(void *ret);
 
 /* Pthread 2LS operations */
 static void pth_sched_entry(void);
@@ -42,8 +41,8 @@ static void pth_thread_runnable(struct uthread *uthread);
 static void pth_thread_paused(struct uthread *uthread);
 static void pth_thread_blockon_sysc(struct uthread *uthread, void *sysc);
 static void pth_thread_has_blocked(struct uthread *uthread, int flags);
-static void pth_thread_refl_fault(struct uthread *uthread, unsigned int trap_nr,
-                                  unsigned int err, unsigned long aux);
+static void pth_thread_refl_fault(struct uthread *uth,
+                                  struct user_context *ctx);
 
 /* Event Handlers */
 static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
@@ -57,143 +56,12 @@ struct schedule_ops pthread_sched_ops = {
        .thread_has_blocked = pth_thread_has_blocked,
        .thread_refl_fault = pth_thread_refl_fault,
 };
-struct signal_ops pthread_signal_ops = {
-       .sigprocmask = pthread_sigmask,
-};
 
 /* Static helpers */
 static void __pthread_free_stack(struct pthread_tcb *pt);
 static int __pthread_allocate_stack(struct pthread_tcb *pt);
 static void __pth_yield_cb(struct uthread *uthread, void *junk);
 
-/* Swap the contents of two user contexts (not just their pointers). */
-static void swap_user_contexts(struct user_context *c1, struct user_context *c2)
-{
-       struct user_context temp_ctx;
-       temp_ctx = *c1;
-       *c1 = *c2;
-       *c2 = temp_ctx;
-}
-
-/* Prep a pthread to run a signal handler.  The original context of the pthread
- * is saved, and a new context with a new stack is set up to run the signal
- * handler the next time the pthread is run. */
-static void __pthread_prep_sighandler(struct pthread_tcb *pthread,
-                                      void (*entry)(void),
-                                      struct siginfo *info)
-{
-       struct user_context *ctx;
-
-       pthread->sigdata = alloc_sigdata();
-       if (info != NULL)
-               pthread->sigdata->info = *info;
-       init_user_ctx(&pthread->sigdata->u_ctx,
-                     (uintptr_t)entry,
-                     (uintptr_t)pthread->sigdata->stack);
-       if (pthread->uthread.flags & UTHREAD_SAVED) {
-               ctx = &pthread->uthread.u_ctx;
-               if (pthread->uthread.flags & UTHREAD_FPSAVED) {
-                       pthread->sigdata->as = pthread->uthread.as;
-                       pthread->uthread.flags &= ~UTHREAD_FPSAVED;
-               }
-       } else {
-               assert(current_uthread == &pthread->uthread);
-               ctx = &vcpd_of(vcore_id())->uthread_ctx;
-               save_fp_state(&pthread->sigdata->as);
-       }
-       swap_user_contexts(ctx, &pthread->sigdata->u_ctx);
-}
-
-/* Restore the context saved as the result of running a signal handler on a
- * pthread. This context will execute the next time the pthread is run. */
-static void __pthread_restore_after_sighandler(struct pthread_tcb *pthread)
-{
-       pthread->uthread.u_ctx = pthread->sigdata->u_ctx;
-       pthread->uthread.flags |= UTHREAD_SAVED;
-       if (pthread->uthread.u_ctx.type == ROS_HW_CTX) {
-               pthread->uthread.as = pthread->sigdata->as;
-               pthread->uthread.flags |= UTHREAD_FPSAVED;
-       }
-       free_sigdata(pthread->sigdata);
-       pthread->sigdata = NULL;
-}
-
-/* Callback when yielding a pthread after upon completion of a sighandler.  We
- * didn't save the current context on yeild, but that's ok because here we
- * restore the original saved context of the pthread and then treat this like a
- * normal voluntary yield. */
-static void __exit_sighandler_cb(struct uthread *uthread, void *junk)
-{
-       __pthread_restore_after_sighandler((struct pthread_tcb*)uthread);
-       __pth_yield_cb(uthread, 0);
-}
-
-/* Run a specific sighandler from the top of the sigdata stack. The 'info'
- * struct is prepopulated before the call is triggered as the result of a
- * reflected fault. */
-static void __run_sighandler()
-{
-       struct pthread_tcb *me = pthread_self();
-       __sigdelset(&me->sigpending, me->sigdata->info.si_signo);
-       trigger_posix_signal(me->sigdata->info.si_signo,
-                            &me->sigdata->info,
-                            &me->sigdata->u_ctx);
-       uthread_yield(FALSE, __exit_sighandler_cb, 0);
-}
-
-/* Run through all pending sighandlers and trigger them with a NULL info field.
- * These handlers are triggered as the result of a pthread_kill(), and thus
- * don't require individual 'info' structs. */
-static void __run_pending_sighandlers()
-{
-       struct pthread_tcb *me = pthread_self();
-       sigset_t andset = me->sigpending & (~me->sigmask);
-       for (int i = 1; i < _NSIG; i++) {
-               if (__sigismember(&andset, i)) {
-                       __sigdelset(&me->sigpending, i);
-                       trigger_posix_signal(i, NULL, &me->sigdata->u_ctx);
-               }
-       }
-       uthread_yield(FALSE, __exit_sighandler_cb, 0);
-}
-
-/* If the given signal is unmasked, prep the pthread to run it's signal
- * handler, but don't run it yet. In either case, make the pthread runnable
- * again. Once the signal handler is complete, the original context will be
- * restored and restarted. */
-static void __pthread_signal_and_restart(struct pthread_tcb *pthread,
-                                          int signo, int code, void *addr)
-{
-       if (!__sigismember(&pthread->sigmask, signo)) {
-               if (pthread->sigdata) {
-                       printf("Pthread sighandler faulted, signal: %d\n", signo);
-                       /* uthread.c already copied out the faulting ctx into the uth */
-                       print_user_context(&pthread->uthread.u_ctx);
-                       exit(-1);
-               }
-               struct siginfo info = {0};
-               info.si_signo = signo;
-               info.si_code = code;
-               info.si_addr = addr;
-               __pthread_prep_sighandler(pthread, __run_sighandler, &info);
-       }
-       pth_thread_runnable(&pthread->uthread);
-}
-
-/* If there are any pending signals, prep the pthread to run it's signal
- * handler. The next time the pthread is run, it will pop into it's signal
- * handler context instead of its original saved context. Once the signal
- * handler is complete, the original context will be restored and restarted. */
-static void __pthread_prep_for_pending_posix_signals(pthread_t pthread)
-{
-       if (!pthread->sigdata && pthread->sigpending) {
-               sigset_t andset = pthread->sigpending & (~pthread->sigmask);
-               if (!__sigisemptyset(&andset)) {
-                       __pthread_prep_sighandler(pthread, __run_pending_sighandlers, 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). */
@@ -203,7 +71,7 @@ static void __attribute__((noreturn)) pth_sched_entry(void)
        if (current_uthread) {
                /* Prep the pthread to run any pending posix signal handlers registered
          * via pthread_kill once it is restored. */
-               __pthread_prep_for_pending_posix_signals((pthread_t)current_uthread);
+               uthread_prep_pending_signals(current_uthread);
                /* Run the thread itself */
                run_current_uthread();
                assert(0);
@@ -220,6 +88,8 @@ static void __attribute__((noreturn)) pth_sched_entry(void)
                new_thread = TAILQ_FIRST(&ready_queue);
                if (new_thread) {
                        TAILQ_REMOVE(&ready_queue, new_thread, tq_next);
+                       assert(new_thread->state == PTH_RUNNABLE);
+                       new_thread->state = PTH_RUNNING;
                        TAILQ_INSERT_TAIL(&active_queue, new_thread, tq_next);
                        threads_active++;
                        threads_ready--;
@@ -236,16 +106,12 @@ static void __attribute__((noreturn)) pth_sched_entry(void)
                /* no new thread, try to yield */
                printd("[P] No threads, vcore %d is yielding\n", vcore_id());
                /* TODO: you can imagine having something smarter here, like spin for a
-                * bit before yielding (or not at all if you want to be greedy). */
-               if (can_adjust_vcores)
-                       vcore_yield(FALSE);
-               if (!parlib_wants_to_be_mcp)
-                       sys_yield(FALSE);
+                * bit before yielding. */
+               vcore_yield(FALSE);
        } while (1);
-       assert(new_thread->state == PTH_RUNNABLE);
        /* Prep the pthread to run any pending posix signal handlers registered
      * via pthread_kill once it is restored. */
-       __pthread_prep_for_pending_posix_signals(new_thread);
+       uthread_prep_pending_signals((struct uthread*)new_thread);
        /* Run the thread itself */
        run_uthread((struct uthread*)new_thread);
        assert(0);
@@ -255,7 +121,7 @@ static void __attribute__((noreturn)) pth_sched_entry(void)
 static void __pthread_run(void)
 {
        struct pthread_tcb *me = pthread_self();
-       pthread_exit(me->start_routine(me->arg));
+       pthread_exit_no_cleanup(me->start_routine(me->arg));
 }
 
 /* GIANT WARNING: if you make any changes to this, also change the broadcast
@@ -278,7 +144,7 @@ static void pth_thread_runnable(struct uthread *uthread)
                        /* can do whatever for each of these cases */
                        break;
                default:
-                       printf("Odd state %d for pthread %08p\n", pthread->state, pthread);
+                       panic("Odd state %d for pthread %08p\n", pthread->state, pthread);
        }
        pthread->state = PTH_RUNNABLE;
        /* Insert the newly created thread into the ready queue of threads.
@@ -290,8 +156,7 @@ static void pth_thread_runnable(struct uthread *uthread)
        mcs_pdr_unlock(&queue_lock);
        /* Smarter schedulers should look at the num_vcores() and how much work is
         * going on to make a decision about how many vcores to request. */
-       if (can_adjust_vcores)
-               vcore_request(threads_ready);
+       vcore_request_more(threads_ready);
 }
 
 /* For some reason not under its control, the uthread stopped running (compared
@@ -307,13 +172,8 @@ static void pth_thread_runnable(struct uthread *uthread)
 static void pth_thread_paused(struct uthread *uthread)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
-       /* Remove from the active list.  Note that I don't particularly care about
-        * the active list.  We keep it around because it causes bugs and keeps us
-        * honest.  After all, some 2LS may want an active list */
-       mcs_pdr_lock(&queue_lock);
-       threads_active--;
-       TAILQ_REMOVE(&active_queue, pthread, tq_next);
-       mcs_pdr_unlock(&queue_lock);
+
+       __pthread_generic_yield(pthread);
        /* communicate to pth_thread_runnable */
        pthread->state = PTH_BLK_PAUSED;
        /* At this point, you could do something clever, like put it at the front of
@@ -367,13 +227,10 @@ static void pth_thread_blockon_sysc(struct uthread *uthread, void *syscall)
        struct syscall *sysc = (struct syscall*)syscall;
        int old_flags;
        uint32_t vcoreid = vcore_id();
-       /* rip from the active queue */
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+
+       __pthread_generic_yield(pthread);
        pthread->state = PTH_BLK_SYSC;
-       mcs_pdr_lock(&queue_lock);
-       threads_active--;
-       TAILQ_REMOVE(&active_queue, pthread, tq_next);
-       mcs_pdr_unlock(&queue_lock);
        /* Set things up so we can wake this thread up later */
        sysc->u_data = uthread;
        /* Register our vcore's syscall ev_q to hear about this syscall. */
@@ -388,6 +245,8 @@ static void pth_thread_blockon_sysc(struct uthread *uthread, void *syscall)
 static void pth_thread_has_blocked(struct uthread *uthread, int flags)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+
+       __pthread_generic_yield(pthread);
        /* could imagine doing something with the flags.  For now, we just treat all
         * externally blocked reasons as 'MUTEX'.  Whatever we do here, we are
         * mostly communicating to our future selves in pth_thread_runnable(), which
@@ -398,18 +257,23 @@ static void pth_thread_has_blocked(struct uthread *uthread, int flags)
                printf("For great justice!\n");
 }
 
+static void __signal_and_restart(struct uthread *uthread,
+                                 int signo, int code, void *addr)
+{
+       uthread_prep_signal_from_fault(uthread, signo, code, addr);
+       pth_thread_runnable(uthread);
+}
+
 static void handle_div_by_zero(struct uthread *uthread, unsigned int err,
                                unsigned long aux)
 {
-       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
-       __pthread_signal_and_restart(pthread, SIGFPE, FPE_INTDIV, (void*)aux);
+       __signal_and_restart(uthread, SIGFPE, FPE_INTDIV, (void*)aux);
 }
 
 static void handle_gp_fault(struct uthread *uthread, unsigned int err,
                             unsigned long aux)
 {
-       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
-       __pthread_signal_and_restart(pthread, SIGSEGV, SEGV_ACCERR, (void*)aux);
+       __signal_and_restart(uthread, SIGSEGV, SEGV_ACCERR, (void*)aux);
 }
 
 static void handle_page_fault(struct uthread *uthread, unsigned int err,
@@ -417,71 +281,59 @@ static void handle_page_fault(struct uthread *uthread, unsigned int err,
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
        if (!(err & PF_VMR_BACKED)) {
-               __pthread_signal_and_restart(pthread, SIGSEGV, SEGV_MAPERR, (void*)aux);
+               __signal_and_restart(uthread, SIGSEGV, SEGV_MAPERR, (void*)aux);
        } else {
-               /* stitching for the event handler.  sysc -> uth, uth -> sysc */
-               uthread->local_sysc.u_data = uthread;
-               uthread->sysc = &uthread->local_sysc;
-               pthread->state = PTH_BLK_SYSC;
-               /* one downside is that we'll never check the return val of the syscall.  if
-                * we errored out, we wouldn't know til we PF'd again, and inspected the old
-                * retval/err and other sysc fields (make sure the PF is on the same addr,
-                * etc).  could run into this issue on truncated files too. */
                syscall_async(&uthread->local_sysc, SYS_populate_va, aux, 1);
-               if (!register_evq(&uthread->local_sysc, sysc_mgmt[vcore_id()].ev_q)) {
-                       /* Lost the race with the call being done.  The kernel won't send the
-                        * event.  Just restart him. */
-                       restart_thread(&uthread->local_sysc);
-               }
+               __block_uthread_on_async_sysc(uthread);
        }
 }
 
-static void pth_thread_refl_fault(struct uthread *uthread, unsigned int trap_nr,
-                                  unsigned int err, unsigned long aux)
+static void pth_thread_refl_hw_fault(struct uthread *uthread,
+                                     unsigned int trap_nr,
+                                     unsigned int err, unsigned long aux)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+
+       __pthread_generic_yield(pthread);
        pthread->state = PTH_BLK_SYSC;
-       mcs_pdr_lock(&queue_lock);
-       threads_active--;
-       TAILQ_REMOVE(&active_queue, pthread, tq_next);
-       mcs_pdr_unlock(&queue_lock);
 
-       /* TODO: RISCV/x86 issue! (0 is divby0, 14 is PF, etc) */
-#if defined(__i386__) || defined(__x86_64__) 
-       switch(trap_nr) {
-               case 0:
-                       handle_div_by_zero(uthread, err, aux);
-                       break;
-               case 13:
-                       handle_gp_fault(uthread, err, aux);
-                       break;
-               case 14:
-                       handle_page_fault(uthread, err, aux);
-                       break;
-               default:
-                       printf("Pthread has unhandled fault: %d, err: %d, aux: %p\n",
-                              trap_nr, err, aux);
-                       /* Note that uthread.c already copied out our ctx into the uth
-                        * struct */
-                       print_user_context(&uthread->u_ctx);
-                       printf("Turn on printx to spew unhandled, malignant trap info\n");
-                       exit(-1);
+       switch (trap_nr) {
+       case HW_TRAP_DIV_ZERO:
+               handle_div_by_zero(uthread, err, aux);
+               break;
+       case HW_TRAP_GP_FAULT:
+               handle_gp_fault(uthread, err, aux);
+               break;
+       case HW_TRAP_PAGE_FAULT:
+               handle_page_fault(uthread, err, aux);
+               break;
+       default:
+               printf("Pthread has unhandled fault: %d, err: %d, aux: %p\n",
+                      trap_nr, err, aux);
+               /* Note that uthread.c already copied out our ctx into the uth
+                * struct */
+               print_user_context(&uthread->u_ctx);
+               printf("Turn on printx to spew unhandled, malignant trap info\n");
+               exit(-1);
        }
-#else
-       #error "Handling hardware faults is currently only supported on x86"
-#endif
 }
 
-/* Akaros pthread extensions / hacks */
-
-/* Tells the pthread 2LS to not change the number of vcores.  This means it will
- * neither request vcores nor yield vcores.  Only used for testing. */
-void pthread_can_vcore_request(bool can)
+static void pth_thread_refl_fault(struct uthread *uth,
+                                  struct user_context *ctx)
 {
-       /* checked when we would request or yield */
-       can_adjust_vcores = can;
+       switch (ctx->type) {
+       case ROS_HW_CTX:
+               pth_thread_refl_hw_fault(uth, __arch_refl_get_nr(ctx),
+                                        __arch_refl_get_err(ctx),
+                                        __arch_refl_get_aux(ctx));
+               break;
+       default:
+               assert(0);
+       }
 }
 
+/* Akaros pthread extensions / hacks */
+
 void pthread_need_tls(bool need)
 {
        need_tls = need;
@@ -603,11 +455,10 @@ void __attribute__((constructor)) pthread_lib_init(void)
        t->state = PTH_RUNNING;
        t->joiner = 0;
        /* implies that sigmasks are longs, which they are. */
-       t->sigmask = 0;
-       __sigemptyset(&t->sigpending);
        assert(t->id == 0);
        t->sched_policy = SCHED_FIFO;
        t->sched_priority = 0;
+       SLIST_INIT(&t->cr_stack);
        /* Put the new pthread (thread0) on the active queue */
        mcs_pdr_lock(&queue_lock);
        threads_active++;
@@ -636,7 +487,7 @@ void __attribute__((constructor)) pthread_lib_init(void)
         * max_vcores()). */
        for (int i = 0; i < max_vcores(); i++) {
                /* Each vcore needs to point to a non-VCPD ev_q */
-               sysc_mgmt[i].ev_q = get_big_event_q_raw();
+               sysc_mgmt[i].ev_q = get_eventq_raw();
                sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR |
                                              EVENT_SPAM_INDIR | EVENT_WAKEUP;
                sysc_mgmt[i].ev_q->ev_vcore = i;
@@ -659,15 +510,14 @@ void __attribute__((constructor)) pthread_lib_init(void)
        sysc_mbox->type = EV_MBOX_UCQ;
        ucq_init_raw(&sysc_mbox->ucq, two_pages, two_pages + PGSIZE);
        for (int i = 0; i < max_vcores(); i++) {
-               sysc_mgmt[i].ev_q = get_event_q();
+               sysc_mgmt[i].ev_q = get_eventq_slim();
                sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR |
                                              EVENT_SPAM_INDIR | EVENT_WAKEUP;
                sysc_mgmt[i].ev_q->ev_vcore = i;
                sysc_mgmt[i].ev_q->ev_mbox = sysc_mbox;
        }
 #endif
-       /* Publish our signal_ops.  Sched ops is set by 2ls_init */
-       signal_ops = &pthread_signal_ops;
+       /* Sched ops is set by 2ls_init */
        uthread_2ls_init((struct uthread*)t, &pthread_sched_ops);
        atomic_init(&threads_total, 1);                 /* one for thread0 */
 }
@@ -678,13 +528,6 @@ void pthread_mcp_init()
        /* Prevent this from happening more than once. */
        init_once_racy(return);
 
-       if (!parlib_wants_to_be_mcp) {
-               /* sign to whether or not we ask for more vcores.  actually, if we're
-                * an SCP, the current kernel will ignore our requests, but best to not
-                * rely on that. */
-               can_adjust_vcores = FALSE;
-               return;
-       }
        uthread_mcp_init();
        /* From here forward we are an MCP running on vcore 0. Could consider doing
         * other pthread specific initialization based on knowing we are an mcp
@@ -714,12 +557,10 @@ 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);
-       pthread->sigdata = NULL;
        /* Might override these later, based on attr && EXPLICIT_SCHED */
        pthread->sched_policy = parent->sched_policy;
        pthread->sched_priority = parent->sched_priority;
+       SLIST_INIT(&pthread->cr_stack);
        /* Respect the attributes */
        if (attr) {
                if (attr->stacksize)                                    /* don't set a 0 stacksize */
@@ -855,14 +696,24 @@ static void __pth_exit_cb(struct uthread *uthread, void *junk)
                exit(0);
 }
 
-void pthread_exit(void *ret)
+static inline void pthread_exit_no_cleanup(void *ret)
 {
        struct pthread_tcb *pthread = pthread_self();
        pthread->retval = ret;
        destroy_dtls();
+       while (SLIST_FIRST(&pthread->cr_stack))
+               pthread_cleanup_pop(FALSE);
        uthread_yield(FALSE, __pth_exit_cb, 0);
 }
 
+void pthread_exit(void *ret)
+{
+       struct pthread_tcb *pthread = pthread_self();
+       while (SLIST_FIRST(&pthread->cr_stack))
+               pthread_cleanup_pop(TRUE);
+       pthread_exit_no_cleanup(ret);
+}
+
 /* Callback/bottom half of yield.  For those writing these pth callbacks, the
  * minimum is call generic, set state (communicate with runnable), then do
  * something that causes it to be runnable in the future (or right now). */
@@ -882,6 +733,34 @@ int pthread_yield(void)
        return 0;
 }
 
+int pthread_cancel(pthread_t __th)
+{
+       fprintf(stderr, "Unsupported %s!", __FUNCTION__);
+       abort();
+       return -1;
+}
+
+void pthread_cleanup_push(void (*routine)(void *), void *arg)
+{
+       struct pthread_tcb *p = pthread_self();
+       struct pthread_cleanup_routine *r = malloc(sizeof(*r));
+       r->routine = routine;
+       r->arg = arg;
+       SLIST_INSERT_HEAD(&p->cr_stack, r, cr_next);
+}
+
+void pthread_cleanup_pop(int execute)
+{
+       struct pthread_tcb *p = pthread_self();
+       struct pthread_cleanup_routine *r = SLIST_FIRST(&p->cr_stack);
+       if (r) {
+               SLIST_REMOVE_HEAD(&p->cr_stack, cr_next);
+               if (execute)
+                       r->routine(r->arg);
+               free(r);
+       }
+}
+
 int pthread_mutexattr_init(pthread_mutexattr_t* attr)
 {
   attr->type = PTHREAD_MUTEX_DEFAULT;
@@ -1024,8 +903,7 @@ static void wake_slist(struct pthread_list *to_wake)
        }
        threads_ready += nr_woken;
        mcs_pdr_unlock(&queue_lock);
-       if (can_adjust_vcores)
-               vcore_request(threads_ready);
+       vcore_request_more(threads_ready);
 }
 
 int pthread_cond_broadcast(pthread_cond_t *c)
@@ -1268,36 +1146,17 @@ int pthread_detach(pthread_t thread)
 
 int pthread_kill(pthread_t thread, int signo)
 {
-       // 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);
+       return uthread_signal(&thread->uthread, signo);
 }
 
 int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
 {
-       sigset_t *sigmask;
-       if (how != SIG_BLOCK && how != SIG_SETMASK && how != SIG_UNBLOCK) {
-               errno = EINVAL;
-               return -1;
-       }
-       sigmask = &((struct pthread_tcb*)current_uthread)->sigmask;
+       int ret = sigprocmask(how, set, oset);
 
-       if (oset)
-               *oset = *sigmask;
-       switch (how) {
-               case SIG_BLOCK:
-                       *sigmask = *sigmask | *set;
-                       break;
-               case SIG_SETMASK:
-                       *sigmask = *set;
-                       break;
-               case SIG_UNBLOCK:
-                       *sigmask = *sigmask & ~(*set);
-                       break;
-       }
-       // Ensures any signals we just unmasked get processed if they are pending
-       pthread_yield();
-       return 0;
+       /* Ensures any pending signals we just unmasked get processed. */
+       if (set && ret == 0)
+               pthread_yield();
+       return ret;
 }
 
 int pthread_sigqueue(pthread_t *thread, int sig, const union sigval value)
@@ -1452,22 +1311,3 @@ int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
        abort();
        return -1;
 }
-
-int pthread_cancel (pthread_t __th)
-{
-       fprintf(stderr, "Unsupported %s!", __FUNCTION__);
-       abort();
-       return -1;
-}
-
-void pthread_cleanup_push(void (*routine)(void *), void *arg)
-{
-       fprintf(stderr, "Unsupported %s!", __FUNCTION__);
-       abort();
-}
-
-void pthread_cleanup_pop(int execute)
-{
-       fprintf(stderr, "Unsupported %s!", __FUNCTION__);
-       abort();
-}