Date: Tue, 17 Aug 2010 14:45:38 +0000 From: David Xu <davidxu@freebsd.org> To: Kostik Belousov <kostikbel@gmail.com> Cc: freebsd-threads@freebsd.org Subject: Re: PTHREAD_CANCEL_DEFERRED Message-ID: <4C6AA092.40708@freebsd.org> In-Reply-To: <20100816104303.GP2396@deviant.kiev.zoral.com.ua> References: <4C642E9B.8000300@freebsd.org> <20100812093353.GS2396@deviant.kiev.zoral.com.ua> <4C650D0F.9060905@freebsd.org> <4C650F27.1000305@freebsd.org> <20100813141402.GW2396@deviant.kiev.zoral.com.ua> <4C65E0FE.2030803@freebsd.org> <20100814144715.GB2396@deviant.kiev.zoral.com.ua> <4C6926D0.2020909@freebsd.org> <20100816082022.GO2396@deviant.kiev.zoral.com.ua> <4C696A96.7020709@freebsd.org> <20100816104303.GP2396@deviant.kiev.zoral.com.ua>
next in thread | previous in thread | raw e-mail | index | archive | help
My idea is to always let close() syscall run, until it will be blocked by lower layer, e.g tty or socket layer, as manual of close() said it always release the file descriptor despite whether it returns EINTR or EWOULDBLOCK, when it is going to sleep, a flag will cause it to abort the sleep. if the thread does not found the flag at that time, a latter signal SIGCANCEL sent by pthread_cancel will unblock it, this is how signal works. A flag called TDP_WAKEUP is checked in sleepq_catch_signals(), if the flag is set, the sleepq_catch_signals return EINTR. In fact, a similar mechanism was already implemented in libthr's condition variable, if you carefully read the code in libthr's sigcancel_handler(), there are following lines: static void sigcancel_handler(int sig __unused, siginfo_t *info __unused, ucontext_t *ucp __unused) { struct pthread *curthread = _get_curthread(); if (curthread->cancel_defer && curthread->cancel_pending) thr_wake(curthread->tid); --- If cancellation is in defer mode and canceling request is pending, the above code turns on kernel thread's flag TDP_WAKEUP, if now a syscall is started, if kernel code finds this flag, it won't sleep and immediately return to userland. However the cancel_defer now is only used by condition variable implementation. it can be used by every cancellation point functions if sleepq_catch_signals checks the flag. As current we do, if the sigcancel_handler found the thread is in asynchronous mode, we immediately kill the thread. Please read the code in kernel function kern_umtx.c:do_cv_wait(), it checks the flag, but I think it is better to make the scope wider, and just let subr_sleepq.c: sleepq_catch_signals() check it, so we can do every cancellation point in cooperative way. Once a cancellation point syscall returns, the code in libthr's function _thr_cancel_leave_defer() checks cancellation request, and cancel the thread, yes, it calls pthread_exit to kill the current thread. it was already implemented in libthr's cond_wait_common() function. If we check TDP_WAKEUP in sleepq_catch_signals, then the close() problem will be resolved, we even can check return value of very cancellation point syscall, for example, if a recv/send syscall return value greater than zero, we may not kill the thread, this is really fun. so the close() syscall wrapper in libthr would looks like this: 1. enter cancellation point 2. call syscall __sys_close() 3. leave cancalltion point, check cancellation request, if set, kill the thread. Finally, I only need to slightly tweak libthr to fix the problem you have found, I don't need to change kernel's signal code at all, no performance problem for thread cancellation. I am working on the patch. Regards, David Xu
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4C6AA092.40708>