Date: Thu, 12 Aug 2010 12:33:53 +0300 From: Kostik Belousov <kostikbel@gmail.com> To: David Xu <davidxu@freebsd.org> Cc: freebsd-threads@freebsd.org Subject: Re: PTHREAD_CANCEL_DEFERRED Message-ID: <20100812093353.GS2396@deviant.kiev.zoral.com.ua> In-Reply-To: <4C642E9B.8000300@freebsd.org> References: <20100811204758.GQ2396@deviant.kiev.zoral.com.ua> <4C63D42D.8040606@freebsd.org> <20100812083006.GR2396@deviant.kiev.zoral.com.ua> <4C642E9B.8000300@freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
--2uzDqHpccQJpqF2n Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Thu, Aug 12, 2010 at 05:25:47PM +0000, David Xu wrote: > 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. >=20 > This might be not enough, because I found a scenario: > suppose there are two threads A and B: >=20 > 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. [1] > B: still doing other things... > B: call close() [2] > B: sleep in kernel because no SIGCANCEL found. >=20 > 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. This situation should be handled by my proposal, since SIGCANCEL is delivered only - at the syscall entry point - at the syscall premature return Userspace would not get SIGCANCEL at time of [1], instead, signal will be delivered at [2]. --2uzDqHpccQJpqF2n Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (FreeBSD) iEYEARECAAYFAkxjwAEACgkQC3+MBN1Mb4jk1QCg96cshPEi1eqB6PqzMYLB0caU C9EAoLXYRM8eQHm0Qxtx/TCiT4ISe4r2 =X/WM -----END PGP SIGNATURE----- --2uzDqHpccQJpqF2n--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20100812093353.GS2396>