Date: Thu, 12 Aug 2010 17:25:47 +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: <4C642E9B.8000300@freebsd.org> In-Reply-To: <20100812083006.GR2396@deviant.kiev.zoral.com.ua> References: <20100811204758.GQ2396@deviant.kiev.zoral.com.ua> <4C63D42D.8040606@freebsd.org> <20100812083006.GR2396@deviant.kiev.zoral.com.ua>
next in thread | previous in thread | raw e-mail | index | archive | help
Kostik Belousov wrote: > On Thu, Aug 12, 2010 at 10:59:57AM +0000, David Xu wrote: >> Kostik Belousov wrote: >>> Hi, >>> Let consider the thread in the state where the cancelation is enabled >>> and cancelation mode set to the PTHREAD_CANCEL_DEFERRED. >>> >>> SUSv4 says the following: >>> Whenever a thread has cancelability enabled and a cancellation request >>> has been made with that thread as the target, and the thread then >>> calls any function that is a cancellation point (such as >>> pthread_testcancel() or read()), the cancellation request shall be >>> acted upon before the function returns. If a thread has cancelability >>> enabled and a cancellation request is made with the thread as a target >>> while the thread is suspended at a cancellation point, the thread >>> shall be awakened and the cancellation request shall be acted upon. >>> >>> Take the close(2) as example, and assume that the cancel is enabled >>> for the thread in deferred mode. libthr wrapper around the syscall >>> executes this: >>> >>> curthread->cancel_point++; >>> testcancel(curthread); >>> __sys_close(fd); >>> curthread->cancel_point--; >>> >>> The pthread_cancel() sends the SIGCANCEL signal to the >>> thread. SIGCANCEL handler calls pthread_exit() if thread has the >>> cancel_point greater then 0. >>> >>> I think this is not right. For instance, thread can be canceled due to >>> SIGCANCEL delivery at the point after the return into the usermode >> >from close(), but before cancel_point is decremented. IMO, the cited >>> statements from the SUSv4 prohibit such behaviour. We should cancel >>> either before closing fd, or during the sleep in close(), but then the >>> syscall should be aborted ("as if signal is delivered"), and again fd >>> should not be closed. >>> >>> Actually, besides not being compliant, I think that the current >>> behaviour is not useful, since in deferred case, we cannot know >>> whether the action by the call that is cancelation point was performed >>> or not. >>> >>> This is not a rant, I probably will fix the issue if it is agreed >>> upon. Opinions ? >> it is true that a cancellation point does not return when cancellation >> happens, so we really does not know if it did something or not, >> and SUSv4 does not say cancellation point is an atomic transaction, >> and whether it will rollback when cancellation happens, the problem may >> happen even if you want to fix it, if the cancellation request is sent >> after int 0x80 instruction returns (on i386) but before libc close() >> stub returns, the cancellation still can happen, so could you tell >> if the file handle is closed or not ? there is no way to tell >> caller that the close() really did something. >> I found the issue when I wrote the code, and it seems Linux does >> it in same way, so I had not done further work on it. > > What I am proposing is to not cancel if close() indeed closed the > descriptor. The same could be done for all other cancelation points that > are syscalls. > > I evaluated the possible implementation. It looks like it is enough, > when thread is switched to cancel enabled and deferred mode, to make > SIGCANCEL delivery special. Namely, it should be only delivered at the > cancelable syscall _entry_ point and on syscall return when error is > EINTR or ERESTART. pthread_testcancel() is also becoming a dummy syscall, > with the same rules of SIGCANCEL delivery. > > Cancelable syscalls would be marked in the syscall table, the cost of > the change is that pthread_setcanceltype() becomes a syscall. This might be not enough, because I found a scenario: suppose there are two threads A and B: B: pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED) A: pthread_cancel(B) it sets cancel_pending to 1 and send SIGCANCEL to B B: got SIGCANCEL delivered to userland and did nothing because it is not at cancel point. B: still doing other things... B: call close() B: sleep in kernel because no SIGCANCEL found. if you don't call testcancel() in close() stub like current libthr did, B won't response to the cancel request, you lost the race.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4C642E9B.8000300>