From owner-freebsd-threads@FreeBSD.ORG Thu Aug 12 09:25:48 2010 Return-Path: Delivered-To: freebsd-threads@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 4F03010656A6 for ; Thu, 12 Aug 2010 09:25:48 +0000 (UTC) (envelope-from davidxu@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 3D57F8FC13; Thu, 12 Aug 2010 09:25:48 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.4/8.14.4) with ESMTP id o7C9Pkmc047440; Thu, 12 Aug 2010 09:25:47 GMT (envelope-from davidxu@freebsd.org) Message-ID: <4C642E9B.8000300@freebsd.org> Date: Thu, 12 Aug 2010 17:25:47 +0000 From: David Xu User-Agent: Thunderbird 2.0.0.24 (X11/20100630) MIME-Version: 1.0 To: Kostik Belousov References: <20100811204758.GQ2396@deviant.kiev.zoral.com.ua> <4C63D42D.8040606@freebsd.org> <20100812083006.GR2396@deviant.kiev.zoral.com.ua> In-Reply-To: <20100812083006.GR2396@deviant.kiev.zoral.com.ua> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Cc: freebsd-threads@freebsd.org Subject: Re: PTHREAD_CANCEL_DEFERRED X-BeenThere: freebsd-threads@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Threading on FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Aug 2010 09:25:48 -0000 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.