add new masked cancellation mode
authorRich Felker <dalias@aerifal.cx>
Sun, 22 Feb 2015 03:05:15 +0000 (22:05 -0500)
committerRich Felker <dalias@aerifal.cx>
Sun, 22 Feb 2015 03:05:15 +0000 (22:05 -0500)
this is a new extension which is presently intended only for
experimental and internal libc use. interface and behavior details may
change subject to feedback and experience from using it internally.

the basic concept for the new PTHREAD_CANCEL_MASKED state is that the
first cancellation point to observe the cancellation request fails
with an errno value of ECANCELED rather than acting on cancellation,
allowing the caller to process the status and choose whether/how to
act upon it.

include/pthread.h
src/thread/pthread_cancel.c
src/thread/pthread_setcancelstate.c

index 2697e8bc10870e06a2b7b18d26d438663abe21fa..99a74a53b5f6b6bc80668db4497a7c9443176a37 100644 (file)
@@ -63,6 +63,7 @@ extern "C" {
 
 #define PTHREAD_CANCEL_ENABLE 0
 #define PTHREAD_CANCEL_DISABLE 1
+#define PTHREAD_CANCEL_MASKED 2
 
 #define PTHREAD_CANCEL_DEFERRED 0
 #define PTHREAD_CANCEL_ASYNCHRONOUS 1
index 66e0817ce79cb833198f3080b92e327780e7d41d..1b71aa423399220f70a0a35066af8b04d8d0339a 100644 (file)
@@ -2,9 +2,13 @@
 #include "syscall.h"
 #include "libc.h"
 
-void __cancel()
+long __cancel()
 {
-       pthread_exit(PTHREAD_CANCELED);
+       pthread_t self = __pthread_self();
+       if (self->canceldisable == PTHREAD_CANCEL_ENABLE || self->cancelasync)
+               pthread_exit(PTHREAD_CANCELED);
+       self->canceldisable = PTHREAD_CANCEL_DISABLE;
+       return -ECANCELED;
 }
 
 /* If __syscall_cp_asm has adjusted the stack pointer, it must provide a
@@ -23,13 +27,16 @@ long __syscall_cp_c(syscall_arg_t nr,
 {
        pthread_t self;
        long r;
+       int st;
 
-       if (!libc.has_thread_pointer || (self = __pthread_self())->canceldisable)
+       if (!libc.has_thread_pointer || (st=(self=__pthread_self())->canceldisable)
+           && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close))
                return __syscall(nr, u, v, w, x, y, z);
 
        r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
-       if (r==-EINTR && nr!=SYS_close && self->cancel && !self->canceldisable)
-               __cancel();
+       if (r==-EINTR && nr!=SYS_close && self->cancel &&
+           self->canceldisable != PTHREAD_CANCEL_DISABLE)
+               r = __cancel();
        return r;
 }
 
@@ -47,14 +54,13 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx)
        extern const char __cp_begin[1], __cp_end[1];
 
        a_barrier();
-       if (!self->cancel || self->canceldisable) return;
+       if (!self->cancel || self->canceldisable == PTHREAD_CANCEL_DISABLE) return;
 
        _sigaddset(&uc->uc_sigmask, SIGCANCEL);
 
        if (self->cancelasync || ip >= __cp_begin && ip < __cp_end) {
-               self->canceldisable = 1;
-               pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, 0);
-               __cancel();
+               ((char **)&uc->uc_mcontext)[CANCEL_REG_IP] = (char *)__cp_cancel;
+               return;
        }
 
        __syscall(SYS_tkill, self->tid, SIGCANCEL);
index 2268217d460a9235651cc7954cc35ed3c76595b9..822a1398914168034c33652adf0512cf142562aa 100644 (file)
@@ -2,7 +2,7 @@
 
 int __pthread_setcancelstate(int new, int *old)
 {
-       if (new > 1U) return EINVAL;
+       if (new > 2U) return EINVAL;
        if (!libc.has_thread_pointer) return ENOSYS;
        struct pthread *self = __pthread_self();
        if (old) *old = self->canceldisable;