Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 04 Dec 2001 15:04:57 -0500
From:      Daniel Eischen <eischen@vigrid.com>
To:        Alfred Perlstein <bright@mu.org>
Cc:        Louis-Philippe Gagnon <louisphilippe@macadamian.com>, freebsd-current@FreeBSD.ORG, freebsd-hackers@FreeBSD.ORG
Subject:   Re: Possible libc_r pthread bug
Message-ID:  <3C0D2C69.8521471D@vigrid.com>
References:  <094601c179ea$7cca85c0$2964a8c0@MACADAMIAN.com> <Pine.SUN.3.91.1011130170847.14642A-100000@pcnet1.pcnet.com> <20011204021815.E92148@elvis.mu.org> <3C0CC2FE.275F4C68@vigrid.com> <20011204114236.H92148@elvis.mu.org> <3C0D1680.E3461FB@gdeb.com> <20011204124624.L92148@elvis.mu.org>

next in thread | previous in thread | raw e-mail | index | archive | help
Alfred Perlstein wrote:
> 
> * Daniel Eischen <deischen@gdeb.com> [011204 12:32] wrote:
> > Alfred Perlstein wrote:
> > >
> > > * Dan Eischen <eischen@vigrid.com> [011204 06:26] wrote:
> > > >
> > > > There are already cancellation tests when resuming threads
> > > > whose contexts are not saved as a result of a signal interrupt
> > > > (ctxtype != CTX_UC). You shouldn't test for cancellation when
> > > > ctxtype == CTX_UC because you are running on the scheduler
> > > > stack, not the threads stack.
> > >
> > > That makes sense, but why?
> >
> > Because when a thread gets cancelled, pthread_exit gets called
> > which then calls the scheduler again.  It is also possible to
> > get interrupted during this process and the threads context
> > (which is operating on the scheduler stack) could get saved.
> > The scheduler could get entered again, and if the thread
> > gets resumed, it'll longjmp to the saved context which is the
> > scheduler stack (and which was just trashed by entering the
> > scheduler again).
> >
> > It is too confusing to try to handle conditions like this, and
> > the threads library doesn't need to get any more confusing ;-)
> > Once the scheduler is entered, no pthread routines should
> > be called and the scheduler should not be recursively
> > entered.  The only way out of the scheduler should be a
> > longjmp or sigreturn to a saved threads context.
> 
> Ok, for the sake of beating a clue into me...
> 
> in uthread_kern.c:_thread_kern_sched
> 
>                 /* Save the state of the current thread: */
>                 if (_setjmp(curthread->ctx.jb) == 0) {
>                         /* Flag the jump buffer was the last state saved: */
>                         curthread->ctxtype = CTX_JB_NOSIG;
>                         curthread->longjmp_val = 1;
>                 } else {
>                         DBG_MSG("Returned from ___longjmp, thread %p\n",
>                             curthread);
>                         /*
>                          * This point is reached when a longjmp() is called
>                          * to restore the state of a thread.
>                          *
>                          * This is the normal way out of the scheduler.
>                          */
>                         _thread_kern_in_sched = 0;
> 
>                         if (curthread->sig_defer_count == 0) {
>                                 if (((curthread->cancelflags &
>                                     PTHREAD_AT_CANCEL_POINT) == 0) &&
>                                     ((curthread->cancelflags &
>                                     PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
>                                         /*
>                                          * Cancellations override signals.
>                                          *
>                                          * Stick a cancellation point at the
>                                          * start of each async-cancellable
>                                          * thread's resumption.
>                                          *
>                                          * We allow threads woken at cancel
>                                          * points to do their own checks.
>                                          */
>                                         pthread_testcancel();
>                         }
> 
> Why isn't this "working", shouldn't it be doing the right thing?
> What if curthread->sig_defer_count wasn't tested?
> Maybe this should be a test against curthread->sig_defer_count <= 1?

Because this is the "normal" way into the scheduler -- when a thread
hits a blocking condition or yields.  A signal interrupting a thread
does not go through this section.  The interrupted threads context is
argument 3 of the signal handler, and this context gets stored in
curthread->ctx.uc.  This is the crux of the problem.  When you resume
this context, you are not in the thread scheduling code and so you
can't check for cancellation.  I'm suggesting that the proper way
to handle this is to munge this interrupted context (a ucontext_t)
so that it first returns to a small wrapper function that can
check for cancellation (and clear the "in scheduler flag" which
is the other problem I mentioned) before returning to the interrupted
context.

There is another way to handle this, but it is more complicated
although probably better than the above method.  This would involve
changing the signal handling code to not use an alternate signal
stack, so an interrupted thread could do something like:

	void
	sighandler(int sig, siginfo_t *info, ucontext_t *ucp)
	{
		...
		{
			ucontext_t	uc;

			/* Save interrupted context on stack: */
			uc = *ucp;
			uc.uc_sigmask = _process_sigmask;

			/* Enter the scheduler: */
			_thread_kern_sched(NULL);

			/*
			 * After return from the scheduler, the
			 * "in scheduler flag" has been cleared
			 * and the thread has been checked for
			 * cancellation.
			 */

			/* Return to interrupted context: */
			__sys_sigreturn(&uc);
		}
	}

The problem is that the signal handling code munges with a threads
saved stack in order to install a signal handler on it.  This is
OK if the thread isn't the current thread.  But if it is the current
thread, then you end up munging with the stack on which you are
currently executing (not a good thing, and this is why we install
an alternate signal stack).  The signal handling code would have to
invoke a signal handler directly if it was for the current thread.
ThreadsNG will probably do it like this.

-- 
Dan Eischen

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3C0D2C69.8521471D>