overhaul pthread cancellation
authorRich Felker <dalias@aerifal.cx>
Sun, 17 Apr 2011 15:43:03 +0000 (11:43 -0400)
committerRich Felker <dalias@aerifal.cx>
Sun, 17 Apr 2011 15:43:03 +0000 (11:43 -0400)
this patch improves the correctness, simplicity, and size of
cancellation-related code. modulo any small errors, it should now be
completely conformant, safe, and resource-leak free.

the notion of entering and exiting cancellation-point context has been
completely eliminated and replaced with alternative syscall assembly
code for cancellable syscalls. the assembly is responsible for setting
up execution context information (stack pointer and address of the
syscall instruction) which the cancellation signal handler can use to
determine whether the interrupted code was in a cancellable state.

these changes eliminate race conditions in the previous generation of
cancellation handling code (whereby a cancellation request received
just prior to the syscall would not be processed, leaving the syscall
to block, potentially indefinitely), and remedy an issue where
non-cancellable syscalls made from signal handlers became cancellable
if the signal handler interrupted a cancellation point.

x86_64 asm is untested and may need a second try to get it right.

50 files changed:
arch/i386/bits/pthread.h
arch/i386/bits/syscall.h
arch/i386/pthread_arch.h
arch/x86_64/bits/syscall.h
arch/x86_64/pthread_arch.h
src/fcntl/fcntl.c
src/fcntl/open.c
src/fcntl/openat.c
src/internal/libc.h
src/internal/pthread_impl.h
src/internal/syscall.h
src/ipc/msgrcv.c
src/ipc/msgsnd.c
src/network/accept.c
src/network/connect.c
src/network/recvfrom.c
src/network/recvmsg.c
src/network/sendmsg.c
src/network/sendto.c
src/process/waitid.c
src/process/waitpid.c
src/select/poll.c
src/select/pselect.c
src/select/select.c
src/signal/sigsuspend.c
src/signal/sigtimedwait.c
src/termios/tcdrain.c
src/thread/__timedwait.c
src/thread/__timedwait_cp.c [new file with mode: 0644]
src/thread/cancel_dummy.c [new file with mode: 0644]
src/thread/cancel_impl.c [new file with mode: 0644]
src/thread/i386/syscall_cp.s [new file with mode: 0644]
src/thread/pthread_cancel.c [deleted file]
src/thread/pthread_cond_timedwait.c
src/thread/pthread_create.c
src/thread/pthread_join.c
src/thread/pthread_testcancel.c
src/thread/sem_timedwait.c
src/thread/syscall_cp.c [new file with mode: 0644]
src/thread/x86_64/syscall_cp.s [new file with mode: 0644]
src/time/clock_nanosleep.c
src/time/nanosleep.c
src/unistd/close.c
src/unistd/pause.c
src/unistd/pread.c
src/unistd/pwrite.c
src/unistd/read.c
src/unistd/readv.c
src/unistd/write.c
src/unistd/writev.c

index 7690ea39b15c7d4d704b67bfee61d08b599264c6..c119dc8a9a4da8876bfa2b15fa7d8f11acbe8d33 100644 (file)
@@ -7,17 +7,17 @@ struct __ptcb {
 
 static inline void __pthread_register_cancel_2(struct __ptcb *__cb)
 {
-       __asm__ __volatile__( "call __pthread_register_cancel" : : "a"(__cb) );
+       __asm__ __volatile__( "call __pthread_register_cancel" : : "a"(__cb) : "ecx", "edx", "memory" );
 }
 
 static inline void __pthread_unregister_cancel_2(struct __ptcb *__cb)
 {
-       __asm__ __volatile__( "call __pthread_unregister_cancel" : : "a"(__cb) );
+       __asm__ __volatile__( "call __pthread_unregister_cancel" : : "a"(__cb) : "ecx", "edx", "memory" );
 }
 
 static inline void __pthread_unwind_next_2(struct __ptcb *__cb)
 {
-       __asm__ __volatile__( "call __pthread_unwind_next" : : "a"(__cb) );
+       __asm__ __volatile__( "call __pthread_unwind_next" : : "a"(__cb) : "ecx", "edx", "memory" );
 }
 
 #define __pthread_register_cancel __pthread_register_cancel_2
index 519e2dcd2d8e953b6d649ed0684bd36965ebc855..274f205ccb81ef2c6b92edcf67be7e415b57397b 100644 (file)
@@ -122,7 +122,9 @@ static inline long __syscall6(long __n, long __a1, long __a2, long __a3, long __
 #define __SC_sendmsg     16
 #define __SC_recvmsg     17
 
-#define __socketcall(nm, a, b, c, d, e, f) syscall(SYS_socketcall, __SC_##nm, \
+#define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_socketcall, __SC_##nm, \
+    ((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f }))
+#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_socketcall, __SC_##nm, \
     ((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f }))
 
 #define __NR_restart_syscall      0
index 64d75cbcc201c8b08f4be1548c03b2287e178d1a..b17dc87abf302f647c4081bb3345034eb952be95 100644 (file)
@@ -5,5 +5,5 @@ static inline struct pthread *__pthread_self()
        return self;
 }
 
-#define PC_AT_SYS(c) \
-       (*(uint16_t *)(((ucontext_t *)(c))->uc_mcontext.__gregs[14])==0x80cd)
+#define CANCEL_REG_SP 7
+#define CANCEL_REG_IP 14
index d18edece31a06b56403e195967f806abe764aaab..21d4c23a7f4805967bd1bfc9d82e7436c61d9851 100644 (file)
@@ -60,7 +60,8 @@ static inline long __syscall6(long __n, long __a1, long __a2, long __a3, long __
        return __ret;
 }
 
-#define __socketcall(nm, a, b, c, d, e, f) syscall(__NR_##nm, a, b, c, d, e, f)
+#define __socketcall(nm,a,b,c,d,e,f) syscall(__NR_##nm, a, b, c, d, e, f)
+#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(__NR_##nm, a, b, c, d, e, f)
 
 #define __NR_read                              0
 #define __NR_write                             1
index af7ae86e2b72f726b81671f120a3cb3d2591add9..c424493a350dd7420dbf5845b2f9462b56774a9d 100644 (file)
@@ -5,5 +5,5 @@ static inline struct pthread *__pthread_self()
        return self;
 }
 
-#define PC_AT_SYS(c) \
-       (*(uint16_t *)(((ucontext_t *)(c))->uc_mcontext.__gregs[16])==0x050f)
+#define CANCEL_REG_SP 15
+#define CANCEL_REG_IP 16
index ab0ebecf32d06051aa5aa3245d920e3db70385ad..2c9fb6f37777961d305fa325d097cf84efeaa4d4 100644 (file)
@@ -6,17 +6,14 @@
 
 int fcntl(int fd, int cmd, ...)
 {
-       int r;
        long arg;
        va_list ap;
        va_start(ap, cmd);
        arg = va_arg(ap, long);
        va_end(ap);
        if (cmd == F_SETFL) arg |= O_LARGEFILE;
-       if (cmd == F_SETLKW) CANCELPT_BEGIN;
-       r = syscall(SYS_fcntl, fd, cmd, arg);
-       if (cmd == F_SETLKW) CANCELPT_END;
-       return r;
+       if (cmd == F_SETLKW) return syscall_cp(SYS_fcntl, fd, cmd, arg);
+       return syscall(SYS_fcntl, fd, cmd, arg);
 }
 
 LFS64(fcntl);
index 064d298c01bcd15cb461d6543282b9563b241f6f..31d6744c753ff125b90f8a0c38dd5db3249c7d44 100644 (file)
@@ -6,16 +6,12 @@
 
 int open(const char *filename, int flags, ...)
 {
-       int r;
        mode_t mode;
        va_list ap;
        va_start(ap, flags);
        mode = va_arg(ap, mode_t);
        va_end(ap);
-       CANCELPT_BEGIN;
-       r = syscall(SYS_open, filename, flags|O_LARGEFILE, mode);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_open, filename, flags|O_LARGEFILE, mode);
 }
 
 LFS64(open);
index 1a2d95350fbe8f2c4bbd94e02a4068467038f364..bdecb8c8fcbbbf1d8258c5c0bab56b54297986fe 100644 (file)
@@ -6,16 +6,12 @@
 
 int openat(int fd, const char *filename, int flags, ...)
 {
-       int r;
        mode_t mode;
        va_list ap;
        va_start(ap, flags);
        mode = va_arg(ap, mode_t);
        va_end(ap);
-       CANCELPT_BEGIN;
-       r = syscall(SYS_openat, fd, filename, flags|O_LARGEFILE, mode);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_openat, fd, filename, flags|O_LARGEFILE, mode);
 }
 
 LFS64(openat);
index c0039e77913a0d79dd09a08e0bd9a729d96030f3..3f1e55e53ac5e4556c2becced197b75e8203f55b 100644 (file)
@@ -6,7 +6,7 @@
 
 struct __libc {
        int *(*errno_location)(void);
-       void (*cancelpt)(int);
+       void (*testcancel)(void);
        void (*lock)(volatile int *);
        void (*lockfile)(FILE *);
        void (*fork_handler)(int);
@@ -40,12 +40,6 @@ void __lock(volatile int *);
 void __lockfile(FILE *);
 #define LOCK(x) (libc.threads_minus_1 ? (__lock(x),1) : ((void)(x),1))
 #define UNLOCK(x) (*(x)=0)
-#define CANCELPT(x) (libc.cancelpt ? libc.cancelpt((x)),0 : (void)(x),0)
-#define CANCELPT_BEGIN CANCELPT(1)
-#define CANCELPT_TRY CANCELPT(0)
-#define CANCELPT_END CANCELPT(-1)
-#define CANCELPT_INHIBIT CANCELPT(2)
-#define CANCELPT_RESUME CANCELPT(-2)
 
 int __rsyscall(int, long, long, long, long, long, long);
 
index a6d90e9bc184e92555fe507d9520c28cd86e1d4a..304bf98d4e5268637ab384ba0e818f337b1a7a16 100644 (file)
@@ -24,7 +24,8 @@ struct pthread {
        unsigned long tlsdesc[4];
        pid_t tid, pid;
        int tsd_used, errno_val, *errno_ptr;
-       volatile int canceldisable, cancelasync, cancelpoint, cancel;
+       volatile uintptr_t cp_sp, cp_ip;
+       volatile int cancel, canceldisable, cancelasync;
        unsigned char *map_base;
        size_t map_size;
        void *start_arg;
@@ -85,6 +86,7 @@ void __lock(volatile int *);
 void __unmapself(void *, size_t);
 
 int __timedwait(volatile int *, int, clockid_t, const struct timespec *, int);
+int __timedwait_cp(volatile int *, int, clockid_t, const struct timespec *, int);
 void __wait(volatile int *, volatile int *, int, int);
 void __wake(volatile int *, int, int);
 
index 39efc42de25cfdf45f7f9ba1164efd65340081da..0c71eca63606e191da234a7027978bff5e180a43 100644 (file)
@@ -6,5 +6,19 @@
 #include <sys/syscall.h>
 
 #define socketcall __socketcall
+#define socketcall_cp __socketcall_cp
+
+#define __syscall_cp0(n) __syscall_cp(n,0,0,0,0,0,0)
+#define __syscall_cp1(n,a) __syscall_cp(n,(long)(a),0,0,0,0,0)
+#define __syscall_cp2(n,a,b) __syscall_cp(n,(long)(a),(long)(b),0,0,0,0)
+#define __syscall_cp3(n,a,b,c) __syscall_cp(n,(long)(a),(long)(b),(long)(c),0,0,0)
+#define __syscall_cp4(n,a,b,c,d) __syscall_cp(n,(long)(a),(long)(b),(long)(c),(long)(d),0,0)
+#define __syscall_cp5(n,a,b,c,d,e) __syscall_cp(n,(long)(a),(long)(b),(long)(c),(long)(d),(long)(e),0)
+#define __syscall_cp6(n,a,b,c,d,e,f) __syscall_cp(n,(long)(a),(long)(b),(long)(c),(long)(d),(long)(e),(long)(f))
+
+long (__syscall_cp)(long, long, long, long, long, long, long);
+
+#define __syscall_cp(...) __SYSCALL_DISP(__syscall_cp,__VA_ARGS__)
+#define syscall_cp(...) __syscall_ret(__syscall_cp(__VA_ARGS__))
 
 #endif
index 13c282eeae8b016bfad13aca92a7dcb1f661fb97..ed4a448a7e5e4f2993f99649345b0850529f9563 100644 (file)
@@ -5,13 +5,9 @@
 
 ssize_t msgrcv(int q, void *m, size_t len, long type, int flag)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
 #ifdef SYS_msgrcv
-       r = syscall(SYS_msgrcv, q, m, len, type, flag);
+       return syscall_cp(SYS_msgrcv, q, m, len, type, flag);
 #else
-       r = syscall(SYS_ipc, IPCOP_msgrcv, q, len, flag, ((long[]){ (long)m, type }));
+       return syscall_cp(SYS_ipc, IPCOP_msgrcv, q, len, flag, ((long[]){ (long)m, type }));
 #endif
-       CANCELPT_END;
-       return r;
 }
index 1e0b2fa50dfc0e7cf52bdd2d3d9e4599d53a4023..23f4a4cbf58cc204343eb8fedb34f83bc2bd4de0 100644 (file)
@@ -5,13 +5,9 @@
 
 int msgsnd(int q, const void *m, size_t len, int flag)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
 #ifdef SYS_msgsnd
-       r = syscall(SYS_msgsnd, q, m, len, flag);
+       return syscall_cp(SYS_msgsnd, q, m, len, flag);
 #else
-       r = syscall(SYS_ipc, IPCOP_msgsnd, q, len, flag, m);
+       return syscall_cp(SYS_ipc, IPCOP_msgsnd, q, len, flag, m);
 #endif
-       CANCELPT_END;
-       return r;
 }
index 46adff5ce9cd254672a424574a0059378aca1bcb..f6b75ba4136e018812407c2f0b584e8fcebf50f8 100644 (file)
@@ -4,9 +4,5 @@
 
 int accept(int fd, struct sockaddr *addr, socklen_t *len)
 {
-       int ret;
-       CANCELPT_BEGIN;
-       ret = socketcall(accept, fd, addr, len, 0, 0, 0);
-       CANCELPT_END;
-       return ret;
+       return socketcall_cp(accept, fd, addr, len, 0, 0, 0);
 }
index 29bffbcb639b9082f169e7cfc6212900c467c35d..57f01a1edbea3a8095091ad3a713591147e454e9 100644 (file)
@@ -4,9 +4,5 @@
 
 int connect(int fd, const struct sockaddr *addr, socklen_t len)
 {
-       int ret;
-       CANCELPT_BEGIN;
-       ret = socketcall(connect, fd, addr, len, 0, 0, 0);
-       CANCELPT_END;
-       return ret;
+       return socketcall_cp(connect, fd, addr, len, 0, 0, 0);
 }
index d5222932efff480e5644c3377bfd9ffe34d378ce..035a15f8a0711fdd096ef4ce95229e6608871d2e 100644 (file)
@@ -4,9 +4,5 @@
 
 ssize_t recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *alen)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
-       r = socketcall(recvfrom, fd, buf, len, flags, addr, alen);
-       CANCELPT_END;
-       return r;
+       return socketcall_cp(recvfrom, fd, buf, len, flags, addr, alen);
 }
index 65094fc8be4b9e08b95a5671f30bdaed6eca4cea..4f526659183defd2405ed6961b8ccca9e8ded1f5 100644 (file)
@@ -14,9 +14,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags)
                msg = &h;
        }
 #endif
-       CANCELPT_BEGIN;
-       r = socketcall(recvmsg, fd, msg, flags, 0, 0, 0);
-       CANCELPT_END;
+       r = socketcall_cp(recvmsg, fd, msg, flags, 0, 0, 0);
 #if LONG_MAX > INT_MAX
        if (orig) *orig = h;
 #endif
index 047c0effa7cd25b9b37c29aa0bb3906212f5ffce..164c28d72a0e40e1b83d1ee6d423437e7d95236b 100644 (file)
@@ -5,7 +5,6 @@
 
 ssize_t sendmsg(int fd, const struct msghdr *msg, int flags)
 {
-       ssize_t r;
 #if LONG_MAX > INT_MAX
        struct msghdr h;
        if (msg) {
@@ -14,8 +13,5 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags)
                msg = &h;
        }
 #endif
-       CANCELPT_BEGIN;
-       r = socketcall(sendmsg, fd, msg, flags, 0, 0, 0);
-       CANCELPT_END;
-       return r;
+       return socketcall_cp(sendmsg, fd, msg, flags, 0, 0, 0);
 }
index 1cfc621d2796c64215a8d086bbbab7145262260c..899eecff57befa5a920d504e8fa01ef0db1b4835 100644 (file)
@@ -4,9 +4,5 @@
 
 ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t alen)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
-       r = socketcall(sendto, fd, buf, len, flags, addr, alen);
-       CANCELPT_END;
-       return r;
+       return socketcall_cp(sendto, fd, buf, len, flags, addr, alen);
 }
index 4fa7c024df4f221a9e859e1b3b8b125f863c16c7..c67feac3836fcfa7487e11560f5990f5021a733c 100644 (file)
@@ -4,10 +4,5 @@
 
 int waitid(idtype_t type, id_t id, siginfo_t *info, int options)
 {
-       int r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_waitid, type, id, info, options, 0);
-       if (r<0) CANCELPT_TRY;
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_waitid, type, id, info, options, 0);
 }
index 5e0320f7b0bb9f2a935d61b73031425a92dddeee..f75e31ef24674df14252163f858aac6e6ddf8ed2 100644 (file)
@@ -4,10 +4,5 @@
 
 pid_t waitpid(pid_t pid, int *status, int options)
 {
-       int r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_wait4, pid, status, options, 0);
-       if (r<0) CANCELPT_TRY;
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_wait4, pid, status, options, 0);
 }
index caceebaf08c819c2beae94d19416db78541dd3e9..f1e73e82f75d076f9c7cd32daed202a4f05908cf 100644 (file)
@@ -4,9 +4,5 @@
 
 int poll(struct pollfd *fds, nfds_t n, int timeout)
 {
-       int r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_poll, fds, n, timeout);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_poll, fds, n, timeout);
 }
index 155a6eb0382ce79a265c9ea10e33d6b89be5e91c..f28887ff25d143c8809554053eb1555496a2ff75 100644 (file)
@@ -4,13 +4,8 @@
 
 int pselect(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, const struct timespec *ts, const sigset_t *mask)
 {
-       int r;
        long data[2] = { (long)mask, 8 };
        struct timespec ts_tmp;
        if (ts) ts_tmp = *ts;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_pselect6, n, rfds, wfds, efds, ts ? &ts_tmp : 0, data);
-       CANCELPT_TRY;
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, ts ? &ts_tmp : 0, data);
 }
index b38e7fd287ca3447a86396cc6cb6d0c3b6a9b421..696cb2880efcf1b2c0607f16cb424b203d6d5055 100644 (file)
@@ -4,10 +4,5 @@
 
 int select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv)
 {
-       int r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_select, n, rfds, wfds, efds, tv);
-       CANCELPT_TRY;
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_select, n, rfds, wfds, efds, tv);
 }
index cec5ddce67cc966862a1c8602cea3b9528d37c6e..cd3a7b59a0d46b216e285ba0d11f7b0697f9719e 100644 (file)
@@ -4,10 +4,5 @@
 
 int sigsuspend(const sigset_t *mask)
 {
-       int ret;
-       CANCELPT_BEGIN;
-       ret = syscall(SYS_rt_sigsuspend, mask, 8);
-       if (ret<0) CANCELPT_TRY;
-       CANCELPT_END;
-       return ret;
+       return syscall_cp(SYS_rt_sigsuspend, mask, 8);
 }
index 1694cbe13cc1e49e985e904e9c38a703639782a2..7eea58ab856b914366bc3a35945a7f8591ac6fd6 100644 (file)
@@ -6,11 +6,7 @@
 int sigtimedwait(const sigset_t *mask, siginfo_t *si, const struct timespec *timeout)
 {
        int ret;
-       CANCELPT_BEGIN;
-       do {
-               ret = syscall(SYS_rt_sigtimedwait, mask, si, timeout, 8);
-               if (ret<0) CANCELPT_TRY;
-       } while (ret<0 && errno==EINTR);
-       CANCELPT_END;
+       do ret = syscall_cp(SYS_rt_sigtimedwait, mask, si, timeout, 8);
+       while (ret<0 && errno==EINTR);
        return ret;
 }
index 9ff1ecd5629537346c720e444a2f6ab2c37284e1..6e43afb7564001143ad096e85cb6ba2a93462262 100644 (file)
@@ -1,13 +1,9 @@
 #include <termios.h>
 #include <sys/ioctl.h>
 #include "libc.h"
+#include "syscall.h"
 
 int tcdrain(int fd)
 {
-       int ret;
-       CANCELPT_BEGIN;
-       ret = ioctl(fd, TCSBRK, 1);
-       CANCELPT_TRY;
-       CANCELPT_END;
-       return ret;
+       return syscall_cp(SYS_ioctl, fd, TCSBRK, 1);
 }
index 5c84e80c1ad7556fc0b94b3c46e4a0fa47e33f00..b1d3af2382fa36ba8715c0731c650873512caff6 100644 (file)
@@ -2,7 +2,7 @@
 #include <errno.h>
 #include "futex.h"
 #include "syscall.h"
-#include <stdio.h>
+
 int __timedwait(volatile int *addr, int val, clockid_t clk, const struct timespec *at, int priv)
 {
        int r;
diff --git a/src/thread/__timedwait_cp.c b/src/thread/__timedwait_cp.c
new file mode 100644 (file)
index 0000000..c289098
--- /dev/null
@@ -0,0 +1,23 @@
+#include <time.h>
+#include <errno.h>
+#include "futex.h"
+#include "syscall.h"
+
+int __timedwait_cp(volatile int *addr, int val, clockid_t clk, const struct timespec *at, int priv)
+{
+       int r;
+       struct timespec to;
+       if (at) {
+               clock_gettime(clk, &to);
+               to.tv_sec = at->tv_sec - to.tv_sec;
+               if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) {
+                       to.tv_sec--;
+                       to.tv_nsec += 1000000000;
+               }
+               if (to.tv_sec < 0) return ETIMEDOUT;
+       }
+       if (priv) priv = 128; priv=0;
+       r = -__syscall_cp(SYS_futex, (long)addr, FUTEX_WAIT | priv, val, at ? (long)&to : 0);
+       if (r == ETIMEDOUT || r == EINTR) return r;
+       return 0;
+}
diff --git a/src/thread/cancel_dummy.c b/src/thread/cancel_dummy.c
new file mode 100644 (file)
index 0000000..a39117e
--- /dev/null
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+static long sccp(long nr, long u, long v, long w, long x, long y, long z)
+{
+       return (__syscall)(nr, u, v, w, x, y, z);
+}
+
+weak_alias(sccp, __syscall_cp);
diff --git a/src/thread/cancel_impl.c b/src/thread/cancel_impl.c
new file mode 100644 (file)
index 0000000..5ce545d
--- /dev/null
@@ -0,0 +1,70 @@
+#include "pthread_impl.h"
+
+long __syscall_cp_asm(volatile void *, long, long, long, long, long, long, long);
+
+long (__syscall_cp)(long nr, long u, long v, long w, long x, long y, long z)
+{
+       pthread_t self;
+       uintptr_t old_sp, old_ip;
+       long r;
+
+       if (!libc.lock || (self = __pthread_self())->canceldisable)
+               return __syscall(nr, u, v, w, x, y, z);
+
+       old_sp = self->cp_sp;
+       old_ip = self->cp_ip;
+       self->cp_sp = 0;
+       self->cp_ip = 0;
+       r = __syscall_cp_asm(&self->cp_sp, nr, u, v, w, x, y, z);
+       self->cp_sp = old_sp;
+       self->cp_ip = old_ip;
+       if (r == -EINTR && self->cancel) pthread_exit(PTHREAD_CANCELED);
+       return r;
+}
+
+static void cancel_handler(int sig, siginfo_t *si, void *ctx)
+{
+       pthread_t self = __pthread_self();
+       ucontext_t *uc = ctx;
+       uintptr_t sp = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_SP];
+       uintptr_t ip = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_IP];
+
+       if (!self->cancel || self->canceldisable) return;
+
+       if (self->cancelasync) pthread_exit(PTHREAD_CANCELED);
+
+       if (sp != self->cp_sp) {
+               if (!sp) return;
+               sigaddset(&uc->uc_sigmask, SIGCANCEL);
+               __syscall(SYS_tgkill, self->pid, self->tid, SIGCANCEL);
+               return;
+       }
+
+       if (ip <= self->cp_ip) pthread_exit(PTHREAD_CANCELED);
+}
+
+static void testcancel()
+{
+       pthread_t self = __pthread_self();
+       if (self->cancel && !self->canceldisable)
+               pthread_exit(PTHREAD_CANCELED);
+}
+
+static void init_cancellation()
+{
+       struct sigaction sa = {
+               .sa_flags = SA_SIGINFO | SA_RESTART,
+               .sa_sigaction = cancel_handler
+       };
+       sigfillset(&sa.sa_mask);
+       __libc_sigaction(SIGCANCEL, &sa, 0);
+       libc.testcancel = testcancel;
+}
+
+int pthread_cancel(pthread_t t)
+{
+       static pthread_once_t once;
+       pthread_once(&once, init_cancellation);
+       a_store(&t->cancel, 1);
+       return pthread_kill(t, SIGCANCEL);
+}
diff --git a/src/thread/i386/syscall_cp.s b/src/thread/i386/syscall_cp.s
new file mode 100644 (file)
index 0000000..6f98a77
--- /dev/null
@@ -0,0 +1,36 @@
+.text
+.global __syscall_cp_asm
+.type   __syscall_cp_asm,%function
+__syscall_cp_asm:
+       pushl %ebx
+       pushl %esi
+       pushl %edi
+       pushl %ebp
+       leal 20(%esp),%ebp
+       call 1f
+1:     popl %eax
+       movl (%ebp),%ecx
+       addl $[1f-1b],%eax
+       movl %eax,4(%ecx)
+       movl %esp,(%ecx)
+       movl 8(%ecx),%eax
+       testl %eax,%eax
+       jnz 2f
+       movl 4(%ebp),%eax
+       movl 8(%ebp),%ebx
+       movl 12(%ebp),%ecx
+       movl 16(%ebp),%edx
+       movl 20(%ebp),%esi
+       movl 24(%ebp),%edi
+       movl 28(%ebp),%ebp
+1:     int $128
+       popl %ebp
+       popl %edi
+       popl %esi
+       popl %ebx
+       ret
+2:     xorl %eax,%eax
+       movl %eax,4(%ecx)
+       movl %eax,(%ecx)
+       pushl $-1
+       call pthread_exit
diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c
deleted file mode 100644 (file)
index c497dbe..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "pthread_impl.h"
-
-int pthread_cancel(pthread_t t)
-{
-       a_store(&t->cancel, 1);
-       return pthread_kill(t, SIGCANCEL);
-}
index 7a19fc5581f1ac0d72bf75409b71f4c060486aff..13728282df2313b8b4ae2f1b19cac0a6ec2c5861 100644 (file)
@@ -8,22 +8,19 @@ static void relock(void *m)
 int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *ts)
 {
        int r, e=0;
-       CANCELPT_BEGIN;
-       CANCELPT_END;
+
+       pthread_testcancel();
 
        pthread_cleanup_push(relock, m);
        c->_c_block = 1;
        if ((r=pthread_mutex_unlock(m))) return r;
 
-       CANCELPT_BEGIN;
-       do e = __timedwait(&c->_c_block, 1, c->_c_clock, ts, 0);
+       do e = __timedwait_cp(&c->_c_block, 1, c->_c_clock, ts, 0);
        while (e == EINTR);
-       CANCELPT_END;
 
        pthread_cleanup_pop(0);
        if ((r=pthread_mutex_lock(m))) return r;
 
-       CANCELPT_BEGIN;
-       CANCELPT_END;
+       pthread_testcancel();
        return e;
 }
index a722a2d6fdbeb0a0e8c9f6e253cd10610c1987b9..844d7bf90e397b1f07b4afc4027b23ca9325cb13 100644 (file)
@@ -19,6 +19,7 @@ weak_alias(dummy_1, __pthread_tsd_run_dtors);
 void __pthread_unwind_next(struct __ptcb *cb)
 {
        pthread_t self;
+       int n;
 
        if (cb->__next) longjmp((void *)cb->__next->__jb, 1);
 
@@ -31,8 +32,9 @@ void __pthread_unwind_next(struct __ptcb *cb)
        /* Mark this thread dead before decrementing count */
        self->dead = 1;
 
-       if (!a_fetch_add(&libc.threads_minus_1, -1))
-               exit(0);
+       do n = libc.threads_minus_1;
+       while (n && a_cas(&libc.threads_minus_1, n, n-1)!=n);
+       if (!n) exit(0);
 
        if (self->detached && self->map_base) {
                __syscall(SYS_rt_sigprocmask, SIG_BLOCK, (long)(uint64_t[1]){-1},0,8);
@@ -42,43 +44,16 @@ void __pthread_unwind_next(struct __ptcb *cb)
        __syscall(SYS_exit, 0);
 }
 
-static void docancel(struct pthread *self)
-{
-       struct __ptcb cb = { .__next = self->cancelbuf };
-       self->canceldisable = 1;
-       self->cancelasync = 0;
-       __pthread_unwind_next(&cb);
-}
-
-static void cancel_handler(int sig, siginfo_t *si, void *ctx)
-{
-       struct pthread *self = __pthread_self();
-       if (self->cancel && !self->canceldisable &&
-           (self->cancelasync || (self->cancelpoint==1 && PC_AT_SYS(ctx))))
-               docancel(self);
-}
-
-static void cancelpt(int x)
-{
-       struct pthread *self = __pthread_self();
-       if ((self->cancelpoint+=x)==1 && self->cancel
-               && x<2U && !self->canceldisable) docancel(self);
-}
-
 static void init_threads()
 {
-       struct sigaction sa = { .sa_flags = SA_SIGINFO | SA_RESTART };
+       sigset_t set;
        libc.lock = __lock;
        libc.lockfile = __lockfile;
-       libc.cancelpt = cancelpt;
-
-       sigemptyset(&sa.sa_mask);
-       sa.sa_sigaction = cancel_handler;
-       __libc_sigaction(SIGCANCEL, &sa, 0);
 
-       sigaddset(&sa.sa_mask, SIGSYSCALL);
-       sigaddset(&sa.sa_mask, SIGCANCEL);
-       __libc_sigprocmask(SIG_UNBLOCK, &sa.sa_mask, 0);
+       sigemptyset(&set);
+       sigaddset(&set, SIGSYSCALL);
+       sigaddset(&set, SIGCANCEL);
+       __libc_sigprocmask(SIG_UNBLOCK, &set, 0);
 }
 
 static int start(void *p)
@@ -159,6 +134,9 @@ int pthread_create(pthread_t *res, const pthread_attr_t *attr, void *(*entry)(vo
 void pthread_exit(void *result)
 {
        struct pthread *self = pthread_self();
+       struct __ptcb cb = { .__next = self->cancelbuf };
        self->result = result;
-       docancel(self);
+       self->canceldisable = 1;
+       self->cancelasync = 0;
+       __pthread_unwind_next(&cb);
 }
index 5210ed4835b760769b3836e4ba0cf4fab03d9b0a..ba7bb7d54be15ca9ad911af060e2dd75f258abc1 100644 (file)
@@ -3,9 +3,7 @@
 int pthread_join(pthread_t t, void **res)
 {
        int tmp = t->tid;
-       CANCELPT_BEGIN;
-       if (tmp) __wait(&t->tid, 0, tmp, 1);
-       CANCELPT_END;
+       if (tmp) __timedwait_cp(&t->tid, tmp, 0, 0, 1);
        if (res) *res = t->result;
        if (t->map_base) munmap(t->map_base, t->map_size);
        return 0;
index 774b70689978f0e351fa30b13976ccf227051f24..c6b250b2ccd6cb1af3a65587b21e229b92e055f8 100644 (file)
@@ -2,6 +2,5 @@
 
 void pthread_testcancel()
 {
-       CANCELPT_BEGIN;
-       CANCELPT_END;
+       if (libc.testcancel) libc.testcancel();
 }
index 4f45c17211bcd1d4da1142c73da44b10e4a7b518..db05b417ca3703c58cea5a5c64148f004b569720 100644 (file)
@@ -21,19 +21,16 @@ int sem_timedwait(sem_t *sem, const struct timespec *at)
        a_inc(sem->__val+1);
        pthread_cleanup_push(cleanup, sem->__val+1)
 
-       CANCELPT_BEGIN;
        for (;;) {
                r = 0;
                if (!sem_trywait(sem)) break;
-               r = __timedwait(sem->__val, 0, CLOCK_REALTIME, at, 0);
+               r = __timedwait_cp(sem->__val, 0, CLOCK_REALTIME, at, 0);
                if (r) {
                        errno = r;
                        r = -1;
                        break;
                }
-               CANCELPT_TRY;
        }
-       CANCELPT_END;
 
        pthread_cleanup_pop(1);
 
diff --git a/src/thread/syscall_cp.c b/src/thread/syscall_cp.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/thread/x86_64/syscall_cp.s b/src/thread/x86_64/syscall_cp.s
new file mode 100644 (file)
index 0000000..1894ce1
--- /dev/null
@@ -0,0 +1,24 @@
+.text
+.global __syscall_cp_asm
+.type   __syscall_cp_asm,%function
+__syscall_cp_asm:
+       lea 1f(%rip),%rax
+       mov %rax,8(%rdi)
+       mov %rsp,(%rdi)
+       mov 16(%rdi),%eax
+       test %eax,%eax
+       jnz 2f
+       mov %rsi,%rax
+       mov %rdx,%rdi
+       mov %rcx,%rsi
+       mov %r8,%rdx
+       mov %r9,%r10
+       mov 8(%rsp),%r8
+       mov 16(%rsp),%r9
+1:     syscall
+       ret
+2:     xor %edi,%edi
+       mov %rdi,8(%r10)
+       mov %rdi,(%r10)
+       dec %rdi
+       jmp pthread_exit
index 4667725b734874817912e4dafbdb78dbc99bd68a..ec87b9e33252c8f0a1cb9f04794c2365354f74db 100644 (file)
@@ -4,9 +4,5 @@
 
 int clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem)
 {
-       int ret;
-       CANCELPT_BEGIN;
-       ret = -__syscall(SYS_clock_nanosleep, clk, flags, req, rem);
-       CANCELPT_END;
-       return ret;
+       return -__syscall_cp(SYS_clock_nanosleep, clk, flags, req, rem);
 }
index 0e0753f3a78a47ee1a6bd862c39e5123ebcef8f5..c8878b11c7184986d3053e4383cbd57e07e976bf 100644 (file)
@@ -5,10 +5,5 @@
 
 int nanosleep(const struct timespec *req, struct timespec *rem)
 {
-       int ret;
-       CANCELPT_BEGIN;
-       ret = syscall(SYS_nanosleep, req, rem);
-       CANCELPT_TRY;
-       CANCELPT_END;
-       return ret;
+       return syscall_cp(SYS_nanosleep, req, rem);
 }
index f52c0ef3a91736264a3c9ae0c06e3182ea44d7dc..231f79ef699cfdeedf72cc42505602d1a1ad7e2f 100644 (file)
@@ -4,8 +4,7 @@
 
 int close(int fd)
 {
-       int ret = syscall(SYS_close, fd);
-       CANCELPT_BEGIN;
-       CANCELPT_END;
+       int ret = syscall_cp(SYS_close, fd);
+       if (libc.testcancel) libc.testcancel();
        return ret;
 }
index 57ed25e5d302c08c4dbdcc815293b2e0b4620e9a..f7ed17d16f0fdc481d1bc6d3df37c72eeb82989d 100644 (file)
@@ -4,9 +4,5 @@
 
 int pause(void)
 {
-       int r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_pause);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_pause);
 }
index 0a0450130e892329c7d8f7c1677de3f4f094aee8..1bf0c754bd0cb8cd5a51ec1f06504138be5a28ac 100644 (file)
@@ -4,11 +4,7 @@
 
 ssize_t pread(int fd, void *buf, size_t size, off_t ofs)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_pread, fd, buf, size, __SYSCALL_LL(ofs));
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_pread, fd, buf, size, __SYSCALL_LL(ofs));
 }
 
 LFS64(pread);
index f878bb63b80d7bd6d90d9c41ce8477a737a11e8f..224eacdd6df01c9ab9ebc0a7965ba20ddc12bf6d 100644 (file)
@@ -4,11 +4,7 @@
 
 ssize_t pwrite(int fd, const void *buf, size_t size, off_t ofs)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_pwrite, fd, buf, size, __SYSCALL_LL(ofs));
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_pwrite, fd, buf, size, __SYSCALL_LL(ofs));
 }
 
 LFS64(pwrite);
index 194b389e860e5ee5b9b9487fa0467e76c23cc4bf..eb882fcc1bf62f5be3115ab2a67c9c78da58531a 100644 (file)
@@ -4,9 +4,5 @@
 
 ssize_t read(int fd, void *buf, size_t count)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_read, fd, buf, count);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_read, fd, buf, count);
 }
index 9b87728e08bdb88bed07c13d5cd4453441edb861..e45cb484f09ae3d361824e160d75e3671fb5ae56 100644 (file)
@@ -4,9 +4,5 @@
 
 ssize_t readv(int fd, const struct iovec *iov, int count)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_readv, fd, iov, count);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_readv, fd, iov, count);
 }
index a8284b3216684c9ed8033b69ae0d73494729f535..e2f7e1f219ad3dedcebe4f1e1362848af72ad5dd 100644 (file)
@@ -4,9 +4,5 @@
 
 ssize_t write(int fd, const void *buf, size_t count)
 {
-       int r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_write, fd, buf, count);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_write, fd, buf, count);
 }
index a45afeb757af56b1ac21c10e901bb47e656d65b4..ef300ddf81a2b107f28e96d448647b1d9463f368 100644 (file)
@@ -4,9 +4,5 @@
 
 ssize_t writev(int fd, const struct iovec *iov, int count)
 {
-       ssize_t r;
-       CANCELPT_BEGIN;
-       r = syscall(SYS_writev, fd, iov, count);
-       CANCELPT_END;
-       return r;
+       return syscall_cp(SYS_writev, fd, iov, count);
 }