Index: sys/kern/subr_sleepqueue.c =================================================================== --- sys/kern/subr_sleepqueue.c (revision 211409) +++ sys/kern/subr_sleepqueue.c (working copy) @@ -408,6 +408,12 @@ sc = SC_LOOKUP(wchan); mtx_assert(&sc->sc_lock, MA_OWNED); MPASS(wchan != NULL); + if ((td->td_pflags & TDP_WAKEUP) != 0) { + td->td_pflags &= ~TDP_WAKEUP; + ret = EINTR; + goto out; + } + /* * See if there are any pending signals for this thread. If not * we can switch immediately. Otherwise do the signal processing @@ -453,6 +459,7 @@ sleepq_switch(wchan, pri); return (0); } +out: /* * There were pending signals and this thread is still * on the sleep queue, remove it from the sleep queue. Index: lib/libthr/thread/thr_sig.c =================================================================== --- lib/libthr/thread/thr_sig.c (revision 211409) +++ lib/libthr/thread/thr_sig.c (working copy) @@ -67,8 +67,6 @@ { struct pthread *curthread = _get_curthread(); - if (curthread->cancel_defer && curthread->cancel_pending) - thr_wake(curthread->tid); curthread->in_sigcancel_handler++; _thr_ast(curthread); curthread->in_sigcancel_handler--; @@ -77,13 +75,46 @@ void _thr_ast(struct pthread *curthread) { - if (!THR_IN_CRITICAL(curthread)) { - _thr_testcancel(curthread); - if (__predict_false((curthread->flags & - (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) - == THR_FLAGS_NEED_SUSPEND)) - _thr_suspend_check(curthread); + + if (THR_IN_CRITICAL(curthread)) + return; + + if (curthread->cancel_pending && curthread->cancel_enable + && !curthread->cancelling) { + if (curthread->cancel_async) { + /* + * asynchronous cancellation mode, act upon + * immediately. + */ + _pthread_exit(PTHREAD_CANCELED); + } else { + /* + * Otherwise, we are in defer mode, and we are at + * cancel point, tell kernel to not block the current + * thread on next cancelable system call. + * + * There are two cases we should call thr_wake() to + * turn on TDP_WAKEUP in kernel: + * 1) we are going to call a cancelable system call, + * non-zero cancel_point means we are already in + * cancelable state, next system call is cancelable. + * 2) because _thr_ast() may be called by + * THR_CRITICAL_LEAVE() which is used by rtld rwlock + * and any libthr internal locks, when rtld rwlock + * is used, it is mostly caused my a unresolved PLT. + * those routines may clear the TDP_WAKEUP flag by + * invoking some system calls, in those cases, we + * also should reenable the flag. + */ + if (curthread->cancel_point) + thr_wake(curthread->tid); + } } + + if (__predict_false((curthread->flags & + (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) + == THR_FLAGS_NEED_SUSPEND)) + _thr_suspend_check(curthread); } void Index: lib/libthr/thread/thr_cond.c =================================================================== --- lib/libthr/thread/thr_cond.c (revision 211409) +++ lib/libthr/thread/thr_cond.c (working copy) @@ -203,7 +203,11 @@ _thr_cancel_enter_defer(curthread); ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 1); info.cond = NULL; - _thr_cancel_leave_defer(curthread, ret); + /* + * if we are truely woken up by others, don't cancel the thread, + * this prevents pthread_cond_signal() from being lost. + */ + _thr_cancel_leave2(curthread, (ret==EINTR)); THR_CLEANUP_POP(curthread, 0); } else { ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 0); Index: lib/libthr/thread/thr_syscalls.c =================================================================== --- lib/libthr/thread/thr_syscalls.c (revision 211409) +++ lib/libthr/thread/thr_syscalls.c (working copy) @@ -167,7 +167,11 @@ curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_accept(s, addr, addrlen); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns a file handle, + * this avoids file descriptor leak. + */ + _thr_cancel_leave2(curthread, ret == -1); return (ret); } @@ -196,7 +200,11 @@ struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter(curthread); + /* + * only cancel the thread after the system call, according to + * manual of close(), the file descriptor is always freed. + */ + _thr_cancel_enter_defer(curthread); ret = __sys_close(fd); _thr_cancel_leave(curthread); @@ -213,7 +221,11 @@ _thr_cancel_enter(curthread); ret = __sys_connect(fd, name, namelen); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the sytem call returns a file handle, + * this avoids file descriptor leak. + */ + _thr_cancel_leave2(curthread, ret == -1); return (ret); } @@ -228,7 +240,11 @@ _thr_cancel_enter(curthread); ret = __creat(path, mode); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the __create returns file handle, + * this avoids file descriptor leak. + */ + _thr_cancel_leave2(curthread, ret == -1); return ret; } @@ -242,6 +258,7 @@ int ret; va_list ap; + /* may cancel the thread */ _thr_cancel_enter(curthread); va_start(ap, cmd); @@ -249,6 +266,9 @@ case F_DUPFD: ret = __sys_fcntl(fd, cmd, va_arg(ap, int)); break; + case F_DUP2FD: + ret = __sys_fcntl(fd, cmd, va_arg(ap, int)); + break; case F_SETFD: case F_SETFL: ret = __sys_fcntl(fd, cmd, va_arg(ap, int)); @@ -266,7 +286,8 @@ } va_end(ap); - _thr_cancel_leave(curthread); + /* ignore thread cancel */ + _thr_cancel_leave2(curthread, 0); return (ret); } @@ -338,9 +359,12 @@ } ret = __sys_open(path, flags, mode); + /* + * don't cancel the thread if the system call returns a file handle, + * this avoids file descriptor leak. + */ + _thr_cancel_leave2(curthread, ret == -1); - _thr_cancel_leave(curthread); - return ret; } @@ -365,9 +389,12 @@ } ret = __sys_openat(fd, path, flags, mode); + /* + * don't cancel the thread if the system call returns a file handle, + * this avoids file descriptor leak. + */ + _thr_cancel_leave2(curthread, ret == -1); - _thr_cancel_leave(curthread); - return ret; } @@ -381,7 +408,10 @@ _thr_cancel_enter(curthread); ret = __sys_poll(fds, nfds, timeout); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns something. + */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -397,7 +427,10 @@ _thr_cancel_enter(curthread); ret = __sys_pselect(count, rfds, wfds, efds, timo, mask); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns something. + */ + _thr_cancel_leave2(curthread, ret==-1); return (ret); } @@ -412,7 +445,8 @@ _thr_cancel_enter(curthread); ret = __sys_read(fd, buf, nbytes); - _thr_cancel_leave(curthread); + /* don't cancel the thread if the system call got data. */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -427,7 +461,8 @@ _thr_cancel_enter(curthread); ret = __sys_readv(fd, iov, iovcnt); - _thr_cancel_leave(curthread); + /* don't cancel the thread if the system call got data. */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -443,7 +478,8 @@ _thr_cancel_enter(curthread); ret = __sys_recvfrom(s, b, l, f, from, fl); - _thr_cancel_leave(curthread); + /* don't cancel the thread if the system call got data. */ + _thr_cancel_leave2(curthread, ret==-1); return (ret); } @@ -457,7 +493,8 @@ _thr_cancel_enter(curthread); ret = __sys_recvmsg(s, m, f); - _thr_cancel_leave(curthread); + /* don't cancel the thread if the system call got data. */ + _thr_cancel_leave2(curthread, ret==-1); return (ret); } @@ -472,7 +509,10 @@ _thr_cancel_enter(curthread); ret = __sys_select(numfds, readfds, writefds, exceptfds, timeout); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns something. + */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -486,7 +526,10 @@ _thr_cancel_enter(curthread); ret = __sys_sendmsg(s, m, f); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call has sent some data. + */ + _thr_cancel_leave2(curthread, ret==-1); return (ret); } @@ -501,7 +544,10 @@ _thr_cancel_enter(curthread); ret = __sys_sendto(s, m, l, f, t, tl); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call has sent some data. + */ + _thr_cancel_leave2(curthread, ret==-1); return (ret); } @@ -575,7 +621,10 @@ _thr_cancel_enter(curthread); ret = __wait(istat); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns a child pid. + */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -590,7 +639,10 @@ _thr_cancel_enter(curthread); ret = _wait4(WAIT_ANY, status, options, rusage); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns a child pid. + */ + _thr_cancel_leave2(curthread, ret==-1); return (ret); } @@ -605,7 +657,10 @@ _thr_cancel_enter(curthread); ret = __sys_wait4(pid, status, options, rusage); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns a child pid. + */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -620,7 +675,10 @@ _thr_cancel_enter(curthread); ret = __waitpid(wpid, status, options); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call returns a child pid. + */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -635,7 +693,10 @@ _thr_cancel_enter(curthread); ret = __sys_write(fd, buf, nbytes); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call has written some data. + */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } @@ -650,7 +711,10 @@ _thr_cancel_enter(curthread); ret = __sys_writev(fd, iov, iovcnt); - _thr_cancel_leave(curthread); + /* + * don't cancel the thread if the system call has written some data. + */ + _thr_cancel_leave2(curthread, ret==-1); return ret; } Index: lib/libthr/thread/thr_join.c =================================================================== --- lib/libthr/thread/thr_join.c (revision 211409) +++ lib/libthr/thread/thr_join.c (working copy) @@ -68,6 +68,11 @@ return (join_common(pthread, thread_return, abstime)); } +/* + * common routine for pthread_join_xxx, pthread_join is a cancellation point, + * this routine returns when there is error or a thread is joined, cancellation + * request is ignored in such cases. + */ static int join_common(pthread_t pthread, void **thread_return, const struct timespec *abstime) @@ -107,6 +112,7 @@ tid = pthread->tid; while (pthread->tid != TID_TERMINATED) { + _thr_testcancel(curthread); if (abstime != NULL) { clock_gettime(CLOCK_REALTIME, &ts); TIMESPEC_SUB(&ts2, abstime, &ts); @@ -122,7 +128,11 @@ break; } - _thr_cancel_leave(curthread); + /* + * if we find the joinee is exited, or there is error, + * don't cancel the joiner. + */ + _thr_cancel_leave2(curthread, 0); THR_CLEANUP_POP(curthread, 0); if (ret == ETIMEDOUT) { Index: lib/libthr/thread/thr_private.h =================================================================== --- lib/libthr/thread/thr_private.h (revision 211409) +++ lib/libthr/thread/thr_private.h (working copy) @@ -368,9 +368,6 @@ /* Thread is at cancellation point */ int cancel_point; - /* Cancellation should be synchoronized */ - int cancel_defer; - /* Asynchronouse cancellation is enabled */ int cancel_async; @@ -645,7 +642,7 @@ void _thr_cancel_enter(struct pthread *) __hidden; void _thr_cancel_leave(struct pthread *) __hidden; void _thr_cancel_enter_defer(struct pthread *) __hidden; -void _thr_cancel_leave_defer(struct pthread *, int) __hidden; +void _thr_cancel_leave2(struct pthread *, int) __hidden; void _thr_testcancel(struct pthread *) __hidden; void _thr_signal_block(struct pthread *) __hidden; void _thr_signal_unblock(struct pthread *) __hidden; Index: lib/libthr/thread/thr_cancel.c =================================================================== --- lib/libthr/thread/thr_cancel.c (revision 211409) +++ lib/libthr/thread/thr_cancel.c (working copy) @@ -42,7 +42,7 @@ testcancel(struct pthread *curthread) { if (__predict_false(SHOULD_CANCEL(curthread) && - !THR_IN_CRITICAL(curthread) && curthread->cancel_defer == 0)) + !THR_IN_CRITICAL(curthread))) _pthread_exit(PTHREAD_CANCELED); } @@ -143,40 +143,32 @@ void _thr_cancel_enter(struct pthread *curthread) { - if (curthread->cancel_enable) { - curthread->cancel_point++; + curthread->cancel_point++; + if (curthread->cancel_enable) testcancel(curthread); - } } void -_thr_cancel_leave(struct pthread *curthread) +_thr_cancel_enter_defer(struct pthread *curthread) { - if (curthread->cancel_enable) - curthread->cancel_point--; + curthread->cancel_point++; + if (__predict_false(SHOULD_CANCEL(curthread) && + !THR_IN_CRITICAL(curthread))) + thr_wake(curthread->tid); } void -_thr_cancel_enter_defer(struct pthread *curthread) +_thr_cancel_leave(struct pthread *curthread) { - if (curthread->cancel_enable) { - curthread->cancel_point++; + if (curthread->cancel_enable) testcancel(curthread); - curthread->cancel_defer++; - } + curthread->cancel_point--; } void -_thr_cancel_leave_defer(struct pthread *curthread, int check) +_thr_cancel_leave2(struct pthread *curthread, int check) { - if (curthread->cancel_enable) { - if (!check) { - curthread->cancel_point--; - curthread->cancel_defer--; - } else { - curthread->cancel_defer--; - testcancel(curthread); - curthread->cancel_point--; - } - } + if (curthread->cancel_enable && check) + testcancel(curthread); + curthread->cancel_point--; }