diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index ce6f32a..4f16b5d 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -360,6 +360,11 @@ FBSD_1.1 { unlinkat; }; +FBSD_1.2 { + thr_cancel_deferred; + thr_cancel_test; +}; + FBSDprivate_1.0 { ___acl_aclcheck_fd; __sys___acl_aclcheck_fd; @@ -952,6 +957,10 @@ FBSDprivate_1.0 { __sys_thr_kill; _thr_kill2; __sys_thr_kill2; + __sys_thr_cancel_deferred; + _thr_cancel_deferred; + __sys_thr_cancel_test; + _thr_cancel_test; _thr_new; __sys_thr_new; _thr_self; diff --git a/lib/libthr/thread/thr_cancel.c b/lib/libthr/thread/thr_cancel.c index bf93fdc..d5d2034 100644 --- a/lib/libthr/thread/thr_cancel.c +++ b/lib/libthr/thread/thr_cancel.c @@ -49,6 +49,7 @@ testcancel(struct pthread *curthread) void _thr_testcancel(struct pthread *curthread) { + testcancel(curthread); } @@ -80,7 +81,7 @@ int _pthread_setcancelstate(int state, int *oldstate) { struct pthread *curthread = _get_curthread(); - int oldval; + int error, oldval; oldval = curthread->cancel_enable; switch (state) { @@ -97,19 +98,28 @@ _pthread_setcancelstate(int state, int *oldstate) default: return (EINVAL); } + error = 0; + if (!curthread->cancel_async) { + if (oldval && !curthread->cancel_enable) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_DISABLE, + NULL); + else if (!oldval && curthread->cancel_enable) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, + NULL); + } if (oldstate) { *oldstate = oldval ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE; } - return (0); + return (error); } int _pthread_setcanceltype(int type, int *oldtype) { - struct pthread *curthread = _get_curthread(); - int oldval; + struct pthread *curthread = _get_curthread(); + int error, oldval; oldval = curthread->cancel_async; switch (type) { @@ -123,12 +133,21 @@ _pthread_setcanceltype(int type, int *oldtype) default: return (EINVAL); } + error = 0; + if (curthread->cancel_enable) { + if (oldval && !curthread->cancel_async) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, + NULL); + else if (!oldval && curthread->cancel_async) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_DISABLE, + NULL); + } if (oldtype) { *oldtype = oldval ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED; } - return (0); + return (error); } void @@ -137,6 +156,8 @@ _pthread_testcancel(void) struct pthread *curthread = _get_curthread(); _thr_cancel_enter(curthread); + if (curthread->cancel_enable && !curthread->cancel_async) + thr_cancel_test(); _thr_cancel_leave(curthread); } diff --git a/lib/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c index f73a6c9..7691891 100644 --- a/lib/libthr/thread/thr_create.c +++ b/lib/libthr/thread/thr_create.c @@ -127,10 +127,9 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr, if (new_thread->attr.flags & PTHREAD_CREATE_DETACHED) new_thread->tlflags |= TLFLAGS_DETACHED; - if (curthread->in_sigcancel_handler) - new_thread->unblock_sigcancel = 1; - else - new_thread->unblock_sigcancel = 0; + new_thread->unblock_sigcancel = curthread->in_sigcancel_handler ? 1 : 0; + new_thread->unblock_siglwpsuspend = + curthread->in_siglwpsuspend_handler ? 1 : 0; /* Add the new thread. */ new_thread->refcount = 1; @@ -168,7 +167,7 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr, SIGDELSET(set, SIGTRAP); __sys_sigprocmask(SIG_SETMASK, &set, &oset); new_thread->sigmask = oset; - SIGDELSET(new_thread->sigmask, SIGCANCEL); + SIGDELSET(new_thread->sigmask, SIGLWPSUSPEND); } ret = thr_new(¶m, sizeof(param)); @@ -247,7 +246,8 @@ create_stack(struct pthread_attr *pattr) static void thread_start(struct pthread *curthread) { - sigset_t set; + sigset_t set, set1; + int unblock; if (curthread->attr.suspend == THR_CREATE_SUSPENDED) set = curthread->sigmask; @@ -263,13 +263,19 @@ thread_start(struct pthread *curthread) if (curthread->force_exit) _pthread_exit(PTHREAD_CANCELED); + thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, NULL); + unblock = 0; + SIGEMPTYSET(set1); if (curthread->unblock_sigcancel) { - sigset_t set1; - - SIGEMPTYSET(set1); SIGADDSET(set1, SIGCANCEL); - __sys_sigprocmask(SIG_UNBLOCK, &set1, NULL); + unblock++; } + if (curthread->unblock_siglwpsuspend) { + SIGADDSET(set1, SIGLWPSUSPEND); + unblock++; + } + if (unblock != 0) + __sys_sigprocmask(SIG_UNBLOCK, &set1, NULL); if (curthread->attr.suspend == THR_CREATE_SUSPENDED) { #if 0 diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c index 1bfdd28..a4bf9b3 100644 --- a/lib/libthr/thread/thr_init.c +++ b/lib/libthr/thread/thr_init.c @@ -353,6 +353,7 @@ _libpthread_init(struct pthread *curthread) _thr_signal_init(); _thr_initial = curthread; SIGDELSET(oldset, SIGCANCEL); + SIGDELSET(oldset, SIGLWPSUSPEND); __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); if (_thread_event_mask & TD_CREATE) _thr_report_creation(curthread, curthread); @@ -408,6 +409,7 @@ init_main_thread(struct pthread *thread) thread->cancel_enable = 1; thread->cancel_async = 0; thr_set_name(thread->tid, "initial thread"); + thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, NULL); /* Initialize the mutex queue: */ TAILQ_INIT(&thread->mutexq); diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index 415c1ec..0d27768 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -78,9 +78,6 @@ typedef TAILQ_HEAD(pthreadlist, pthread) pthreadlist; typedef TAILQ_HEAD(atfork_head, pthread_atfork) atfork_head; TAILQ_HEAD(mutex_queue, pthread_mutex); -/* Signal to do cancellation */ -#define SIGCANCEL 32 - /* * Kernel fatal error handler macro. */ @@ -386,6 +383,12 @@ struct pthread { /* New thread should unblock SIGCANCEL. */ int unblock_sigcancel; + /* Thread is in SIGLWPSUSPEND handler. */ + int in_siglwpsuspend_handler; + + /* New thread should unblock SIGLWPSUSPEND. */ + int unblock_siglwpsuspend; + /* Force new thread to exit. */ int force_exit; diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c index efd8cb4..8a8cffb 100644 --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -63,7 +63,7 @@ int __sigsuspend(const sigset_t *sigmask); static void sigcancel_handler(int sig __unused, - siginfo_t *info __unused, ucontext_t *ucp __unused) + siginfo_t *info __unused, ucontext_t *ucp __unused) { struct pthread *curthread = _get_curthread(); @@ -74,6 +74,17 @@ sigcancel_handler(int sig __unused, curthread->in_sigcancel_handler--; } +static void +siglwpsuspend_handler(int sig __unused, + siginfo_t *info __unused, ucontext_t *ucp __unused) +{ + struct pthread *curthread = _get_curthread(); + + curthread->in_siglwpsuspend_handler++; + _thr_ast(curthread); + curthread->in_siglwpsuspend_handler--; +} + void _thr_ast(struct pthread *curthread) { @@ -97,7 +108,7 @@ _thr_suspend_check(struct pthread *curthread) err = errno; /* - * Blocks SIGCANCEL which other threads must send. + * Blocks SIGLWPSUSPEND which other threads must send. */ _thr_signal_block(curthread); @@ -132,11 +143,12 @@ _thr_suspend_check(struct pthread *curthread) curthread->critical_count--; /* - * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and - * a new signal frame will nest us, this seems a problem because - * stack will grow and overflow, but because kernel will automatically - * mask the SIGCANCEL when delivering the signal, so we at most only - * have one nesting signal frame, this should be fine. + * Unblocks SIGLWPSUSPEND, it is possible a new SIGLWPSUSPEND + * is ready and a new signal frame will nest us, this seems a + * problem because stack will grow and overflow, but because + * kernel will automatically mask the SIGLWPSUSPEND when + * delivering the signal, so we at most only have one nesting + * signal frame, this should be fine. */ _thr_signal_unblock(curthread); errno = err; @@ -152,6 +164,8 @@ _thr_signal_init(void) act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler; __sys_sigaction(SIGCANCEL, &act, NULL); + act.sa_sigaction = (__siginfohandler_t *)&siglwpsuspend_handler; + __sys_sigaction(SIGLWPSUSPEND, &act, NULL); } void @@ -194,7 +208,7 @@ int _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { /* Check if the signal number is out of range: */ - if (!_SIG_VALID(sig) || sig == SIGCANCEL) { + if (!_SIG_VALID(sig) || sig == SIGCANCEL || sig == SIGLWPSUSPEND) { /* Return an invalid argument: */ errno = EINVAL; return (-1); @@ -215,6 +229,7 @@ _sigprocmask(int how, const sigset_t *set, sigset_t *oset) if (set != NULL) { newset = *set; SIGDELSET(newset, SIGCANCEL); + SIGDELSET(newset, SIGLWPSUSPEND); p = &newset; } } @@ -233,23 +248,27 @@ _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) __weak_reference(__sigsuspend, sigsuspend); -int -_sigsuspend(const sigset_t * set) +static const sigset_t * +thr_remove_thr_signals(const sigset_t *set, sigset_t *newset) { - sigset_t newset; const sigset_t *pset; - int ret; - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; + if (SIGISMEMBER(*set, SIGCANCEL) || SIGISMEMBER(*set, SIGLWPSUSPEND)) { + *newset = *set; + SIGDELSET(*newset, SIGCANCEL); + SIGDELSET(*newset, SIGLWPSUSPEND); + pset = newset; } else pset = set; + return (pset); +} - ret = __sys_sigsuspend(pset); +int +_sigsuspend(const sigset_t * set) +{ + sigset_t newset; - return (ret); + return (__sys_sigsuspend(thr_remove_thr_signals(set, &newset))); } int @@ -257,18 +276,10 @@ __sigsuspend(const sigset_t * set) { struct pthread *curthread = _get_curthread(); sigset_t newset; - const sigset_t *pset; int ret; - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; - } else - pset = set; - _thr_cancel_enter(curthread); - ret = __sys_sigsuspend(pset); + ret = __sys_sigsuspend(thr_remove_thr_signals(set, &newset)); _thr_cancel_leave(curthread); return (ret); @@ -283,17 +294,9 @@ _sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout) { sigset_t newset; - const sigset_t *pset; - int ret; - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; - } else - pset = set; - ret = __sys_sigtimedwait(pset, info, timeout); - return (ret); + return (__sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, + timeout)); } int @@ -302,17 +305,11 @@ __sigtimedwait(const sigset_t *set, siginfo_t *info, { struct pthread *curthread = _get_curthread(); sigset_t newset; - const sigset_t *pset; int ret; - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; - } else - pset = set; _thr_cancel_enter(curthread); - ret = __sys_sigtimedwait(pset, info, timeout); + ret = __sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, + timeout); _thr_cancel_leave(curthread); return (ret); } @@ -321,18 +318,8 @@ int _sigwaitinfo(const sigset_t *set, siginfo_t *info) { sigset_t newset; - const sigset_t *pset; - int ret; - - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; - } else - pset = set; - ret = __sys_sigwaitinfo(pset, info); - return (ret); + return (__sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info)); } int @@ -340,18 +327,10 @@ __sigwaitinfo(const sigset_t *set, siginfo_t *info) { struct pthread *curthread = _get_curthread(); sigset_t newset; - const sigset_t *pset; int ret; - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; - } else - pset = set; - _thr_cancel_enter(curthread); - ret = __sys_sigwaitinfo(pset, info); + ret = __sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info); _thr_cancel_leave(curthread); return (ret); } @@ -360,18 +339,8 @@ int _sigwait(const sigset_t *set, int *sig) { sigset_t newset; - const sigset_t *pset; - int ret; - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; - } else - pset = set; - - ret = __sys_sigwait(pset, sig); - return (ret); + return (__sys_sigwait(thr_remove_thr_signals(set, &newset), sig)); } int @@ -379,18 +348,10 @@ __sigwait(const sigset_t *set, int *sig) { struct pthread *curthread = _get_curthread(); sigset_t newset; - const sigset_t *pset; int ret; - if (SIGISMEMBER(*set, SIGCANCEL)) { - newset = *set; - SIGDELSET(newset, SIGCANCEL); - pset = &newset; - } else - pset = set; - _thr_cancel_enter(curthread); - ret = __sys_sigwait(pset, sig); + ret = __sys_sigwait(thr_remove_thr_signals(set, &newset), sig); _thr_cancel_leave(curthread); return (ret); } diff --git a/lib/libthr/thread/thr_suspend_np.c b/lib/libthr/thread/thr_suspend_np.c index 7d4a2e2..dc75c91 100644 --- a/lib/libthr/thread/thr_suspend_np.c +++ b/lib/libthr/thread/thr_suspend_np.c @@ -87,7 +87,7 @@ _pthread_suspend_all_np(void) THR_THREAD_UNLOCK(curthread, thread); } } - thr_kill(-1, SIGCANCEL); + thr_kill(-1, SIGLWPSUSPEND); restart: TAILQ_FOREACH(thread, &_thread_list, tle) { @@ -128,7 +128,7 @@ suspend_common(struct pthread *curthread, struct pthread *thread, thread->flags |= THR_FLAGS_NEED_SUSPEND; tmp = thread->cycle; THR_THREAD_UNLOCK(curthread, thread); - _thr_send_sig(thread, SIGCANCEL); + _thr_send_sig(thread, SIGLWPSUSPEND); if (waitok) { _thr_umtx_wait_uint(&thread->cycle, tmp, NULL, 0); THR_THREAD_LOCK(curthread, thread); diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 4f1fc28..8726a54 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -41,6 +41,7 @@ ; NOPROTO same as STD except do not create structure or ; function prototype in sys/sysproto.h. Does add a ; definition to syscall.h besides adding a sysent. +; CANCEL syscall is the cancelation point ; #ifdef's, etc. may be included, and are copied to the output files. @@ -72,7 +73,7 @@ size_t nbyte); } 5 AUE_OPEN_RWTC NOPROTO { int open(char *path, int flags, \ int mode); } -6 AUE_CLOSE NOPROTO { int close(int fd); } +6 AUE_CLOSE NOPROTO|CANCEL { int close(int fd); } 7 AUE_WAIT4 STD { int freebsd32_wait4(int pid, int *status, \ int options, struct rusage32 *rusage); } 8 AUE_CREAT OBSOL old creat @@ -962,3 +963,6 @@ fd_set *ou, fd_set *ex, \ const struct timespec32 *ts, \ const sigset_t *sm); } +523 AUE_NULL NOPROTO { int thr_cancel_deferred(uint32_t op, \ + uint32_t *val); } +524 AUE_NULL NOPROTO|CANCEL {int thr_cancel_test(void); } \ No newline at end of file diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 8a322de..544edfa 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1478,6 +1478,10 @@ kern_sigsuspend(struct thread *td, sigset_t mask) kern_sigprocmask(td, SIG_SETMASK, &mask, &td->td_oldsigmask, SIGPROCMASK_PROC_LOCKED); td->td_pflags |= TDP_OLDMASK; + if ((td->td_pflags & TDP_CANCELDF) != 0) { + td->td_pflags |= TDP_CANCELPOINT; + SIGDELSET(td->td_sigmask, SIGCANCEL); + } /* * Process signals now. Otherwise, we can get spurious wakeup @@ -1497,6 +1501,15 @@ kern_sigsuspend(struct thread *td, sigset_t mask) mtx_unlock(&p->p_sigacts->ps_mtx); } PROC_UNLOCK(p); + + /* + * This allows ast() to deliver SIGCANCEL even despite return + * code from the sigsuspend() is EJUSTRETURN, that prevents + * the check_cancel() call from syscall_enter().. + */ + if ((td->td_pflags & TDP_CANCELDF) != 0) + td->td_pflags |= TDP_CANCELPOINT; + return (EJUSTRETURN); } @@ -2535,6 +2548,16 @@ issignal(struct thread *td, int stop_allowed) SIGSETOR(sigpending, p->p_sigqueue.sq_signals); SIGSETNAND(sigpending, td->td_sigmask); + /* + * When in the deferred cancelation mode, SIGCANCEL is + * only delivered at the cancelation points. + * Otherwise, it is a normal signal. + * If the debugger decides to send SIGCANCEL, so be it. + */ + if ((td->td_pflags & TDP_CANCELDF) != 0 && + (td->td_pflags & TDP_CANCELPOINT) == 0) + SIGDELSET(sigpending, SIGCANCEL); + if (p->p_flag & P_PPWAIT) SIG_STOPSIGMASK(sigpending); if (SIGISEMPTY(sigpending)) /* no signal to send */ @@ -2556,6 +2579,7 @@ issignal(struct thread *td, int stop_allowed) sigqueue_delete(&p->p_sigqueue, sig); continue; } + if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { /* * If traced, always stop. diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index d8f7a8e..4bd96df 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -522,3 +522,54 @@ thr_set_name(struct thread *td, struct thr_set_name_args *uap) PROC_UNLOCK(p); return (error); } + +int +thr_cancel_deferred(struct thread *td, struct thr_cancel_deferred_args *uap) +{ + struct proc *p; + int error; + + error = 0; + p = td->td_proc; + switch (uap->op) { + case THR_CANCEL_DEFERRED_ENABLE: + if (uap->val != NULL) { + error = suword32(uap->val, !!(td->td_pflags & + TDP_CANCELDF)); + if (error != 0) + break; + } + td->td_pflags |= TDP_CANCELDF; + PROC_LOCK(p); + SIGADDSET(td->td_sigmask, SIGCANCEL); + PROC_UNLOCK(p); + break; + + case THR_CANCEL_DEFERRED_DISABLE: + if (uap->val != NULL) { + error = suword32(uap->val, !!(td->td_pflags & + TDP_CANCELDF)); + if (error != 0) + break; + } + td->td_pflags &= ~TDP_CANCELDF; + PROC_LOCK(p); + SIGDELSET(td->td_sigmask, SIGCANCEL); + PROC_UNLOCK(p); + break; + + default: + error = EINVAL; + break; + } + + return (error); +} + +int +thr_cancel_test(struct thread *td __unused, + struct thr_cancel_test_args *uap __unused) +{ + + return (0); +} diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh index 46e04fc..af37cc1 100644 --- a/sys/kern/makesyscalls.sh +++ b/sys/kern/makesyscalls.sh @@ -257,6 +257,10 @@ s/\$//g if (flag("NOTSTATIC")) { thr_flag = "SY_THR_ABSENT" } + sy_flags = "0" + if (flag("CANCEL")) { + sy_flags = "SY_FLAG_CANCEL_POINT" + } if ($NF != "}") { funcalias=$(NF-2) argalias=$(NF-1) @@ -347,13 +351,6 @@ s/\$//g auditev = $2; } - # - # The currently-empty flags field. - # - { - flags = "0"; - } - type("STD") || type("NODEF") || type("NOARGS") || type("NOPROTO") \ || type("NOSTD") { parseline() @@ -408,8 +405,8 @@ s/\$//g printf("%s },", "lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT") > sysent column = column + length("lkmressys") + length("AUE_NULL") + 3 } else { - printf("%s, %s, NULL, 0, 0, %s, %s },", funcname, auditev, flags, thr_flag) > sysent - column = column + length(funcname) + length(auditev) + length(flags) + 3 + printf("%s, %s, NULL, 0, 0, %s, %s },", funcname, auditev, sy_flags, thr_flag) > sysent + column = column + length(funcname) + length(auditev) + length(sy_flags) + 3 } align_sysent_comment(column) printf("/* %d = %s */\n", syscall, funcalias) > sysent @@ -482,10 +479,10 @@ s/\$//g length("lkmressys") + length("AUE_NULL") + 3) } else { printf("\t{ %s(%s,%s), %s, NULL, 0, 0, %s, %s },", - wrap, argssize, funcname, auditev, flags, thr_flag) > sysent + wrap, argssize, funcname, auditev, sy_flags, thr_flag) > sysent align_sysent_comment(8 + 9 + length(argssize) + 1 + \ length(funcname) + length(auditev) + \ - length(flags) + 4) + length(sy_flags) + 4) } printf("/* %d = %s %s */\n", syscall, descr, funcalias) > sysent printf("\t\"%s.%s\",\t\t/* %d = %s %s */\n", diff --git a/sys/kern/subr_trap.c b/sys/kern/subr_trap.c index 5c90d95..dda1aa9 100644 --- a/sys/kern/subr_trap.c +++ b/sys/kern/subr_trap.c @@ -235,12 +235,44 @@ ast(struct trapframe *framep) td->td_pflags &= ~TDP_OLDMASK; kern_sigprocmask(td, SIG_SETMASK, &td->td_oldsigmask, NULL, 0); } + if ((td->td_pflags & TDP_CANCELDF) != 0) { + PROC_LOCK(p); + SIGADDSET(td->td_sigmask, SIGCANCEL); + PROC_UNLOCK(p); + } + td->td_pflags &= ~TDP_CANCELPOINT; userret(td, framep); mtx_assert(&Giant, MA_NOTOWNED); } #ifdef HAVE_SYSCALL_ARGS_DEF +static int +check_cancel(struct thread *td, struct sysent *callp, int serror) +{ + struct proc *p; + int error; + + if ((td->td_pflags & TDP_CANCELDF) == 0 || + (callp->sy_flags & SY_FLAG_CANCEL_POINT) == 0 || + !(serror == EINTR || serror == ERESTART)) { + td->td_pflags &= ~TDP_CANCELPOINT; + return (0); + } + + p = td->td_proc; + PROC_LOCK(p); + SIGDELSET(td->td_sigmask, SIGCANCEL); + if (SIGISMEMBER(td->td_siglist, SIGCANCEL)) { + error = EINTR; + signotify(td); + } else + error = 0; + PROC_UNLOCK(p); + td->td_pflags |= TDP_CANCELPOINT; + return (error); +} + const char * syscallname(struct proc *p, u_int code) { @@ -300,6 +332,9 @@ syscallenter(struct thread *td, struct syscall_args *sa) if (error != 0) goto retval; } + error = check_cancel(td, sa->callp, EINTR); + if (error != 0) + goto retval; error = syscall_thread_enter(td, sa->callp); if (error != 0) goto retval; @@ -343,6 +378,7 @@ syscallenter(struct thread *td, struct syscall_args *sa) PROC_UNLOCK(p); } (p->p_sysent->sv_set_syscall_retval)(td, error); + check_cancel(td, sa->callp, error); return (error); } diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 8e11134..44760e6 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -41,6 +41,7 @@ ; function prototype in sys/sysproto.h. Does add a ; definition to syscall.h besides adding a sysent. ; NONSTATIC syscall is loadable +; CANCEL syscall is the cancelation point ; ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master @@ -69,7 +70,7 @@ ; XXX should be { int open(const char *path, int flags, ...); } ; but we're not ready for `const' or varargs. ; XXX man page says `mode_t mode'. -6 AUE_CLOSE STD { int close(int fd); } +6 AUE_CLOSE STD|CANCEL { int close(int fd); } 7 AUE_WAIT4 STD { int wait4(int pid, int *status, \ int options, struct rusage *rusage); } \ wait4 wait_args int @@ -922,5 +923,8 @@ fd_set *ou, fd_set *ex, \ const struct timespec *ts, \ const sigset_t *sm); } +523 AUE_NULL STD { int thr_cancel_deferred(uint32_t op, \ + uint32_t *val); } +524 AUE_NULL STD|CANCEL {int thr_cancel_test(void); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/sys/aio.h b/sys/sys/aio.h index 5a8779b..9969e7c 100644 --- a/sys/sys/aio.h +++ b/sys/sys/aio.h @@ -97,7 +97,8 @@ int aio_write(struct aiocb *); * "acb_list" is an array of "nacb_listent" I/O control blocks. * when all I/Os are complete, the optional signal "sig" is sent. */ -int lio_listio(int, struct aiocb * const [], int, struct sigevent *); +int lio_listio(int, struct aiocb * const [], int, + struct sigevent * __restrict); /* * Get completion status @@ -122,7 +123,8 @@ int aio_cancel(int, struct aiocb *); /* * Suspend until all specified I/O or timeout is complete. */ -int aio_suspend(const struct aiocb * const[], int, const struct timespec *); +int aio_suspend(const struct aiocb * const[], int, + const struct timespec * __restrict); #ifdef __BSD_VISIBLE int aio_waitcomplete(struct aiocb **, struct timespec *); diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 336279e..90a3a48 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -383,11 +383,11 @@ do { \ #define TDP_COWINPROGRESS 0x00000010 /* Snapshot copy-on-write in progress. */ #define TDP_ALTSTACK 0x00000020 /* Have alternate signal stack. */ #define TDP_DEADLKTREAT 0x00000040 /* Lock aquisition - deadlock treatment. */ -#define TDP_UNUSED80 0x00000080 /* available. */ +#define TDP_CANCELPOINT 0x00000080 /* Deliver SIGCANCEL. */ #define TDP_NOSLEEPING 0x00000100 /* Thread is not allowed to sleep on a sq. */ #define TDP_OWEUPC 0x00000200 /* Call addupc() at next AST. */ #define TDP_ITHREAD 0x00000400 /* Thread is an interrupt thread. */ -#define TDP_UNUSED800 0x00000800 /* available. */ +#define TDP_CANCELDF 0x00000800 /* In deferred cancel mode. */ #define TDP_SCHED1 0x00001000 /* Reserved for scheduler private use */ #define TDP_SCHED2 0x00002000 /* Reserved for scheduler private use */ #define TDP_SCHED3 0x00004000 /* Reserved for scheduler private use */ diff --git a/sys/sys/signal.h b/sys/sys/signal.h index a7ca96e..7f20a93 100644 --- a/sys/sys/signal.h +++ b/sys/sys/signal.h @@ -111,6 +111,8 @@ #if __BSD_VISIBLE #define SIGTHR 32 /* reserved by thread library. */ #define SIGLWP SIGTHR +#define SIGCANCEL SIGTHR /* Signal to do cancellation */ +#define SIGLWPSUSPEND 33 /* Thread suspension */ #endif #define SIGRTMIN 65 diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index 4754f54..8546b12 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -64,6 +64,8 @@ struct sysent { /* system call table */ u_int32_t sy_thrcnt; }; +#define SY_FLAG_CANCEL_POINT 0x0001 + #define SY_THR_FLAGMASK 0x7 #define SY_THR_STATIC 0x1 #define SY_THR_DRAINING 0x2 diff --git a/sys/sys/thr.h b/sys/sys/thr.h index 7ccc872..dcfe81f 100644 --- a/sys/sys/thr.h +++ b/sys/sys/thr.h @@ -58,6 +58,9 @@ struct thr_param { void *spare[3]; /* TODO: cpu affinity mask etc. */ }; +#define THR_CANCEL_DEFERRED_ENABLE 1 +#define THR_CANCEL_DEFERRED_DISABLE 2 + /* * See pthread_* */ @@ -79,6 +82,8 @@ int thr_kill2(pid_t pid, long id, int sig); int thr_suspend(const struct timespec *timeout); int thr_wake(long id); int thr_set_name(long id, const char *name); +int thr_cancel_deferred(uint32_t op, uint32_t *val); +int thr_cancel_test(void); __END_DECLS #endif /* !_KERNEL */