Subject: Re: So I'll bite. M:N w/o SA - how? (stick to 1:N with Pth!)
To: NetBSD Kernel Technical Discussion List <tech-kern@NetBSD.org>
From: Greg A. Woods <woods@weird.com>
List: tech-kern
Date: 02/22/2007 15:02:09
At Thu, 22 Feb 2007 10:42:51 -0800,
Bucky Katz wrote:
> 
> Joerg Sonnenberger <joerg@britannica.bec.de> writes:
> 
> > On Wed, Feb 21, 2007 at 11:40:24AM -0800, Bucky Katz wrote:
> >> Bill Studenmund <wrstuden@netbsd.org> writes:
> >> > When a thread blocks in the kernel, you'd like to run something
> >> > else on that thread, no?
> >> 
> >> Yes. This is why I don't believe you can accomplish this entirely
> >> in userland.
> >
> > That's the point where I disagree. I do not think it is necessary to do
> > that or even helpful. Let blocking events block the lwp.  When you took
> > the time for a trap, the context switching overhead is reasonably small.
> 
> In the uniprocessor case M:N degenerates to N:1, in practice. If I let
> the 1 lwp block, how do I get any of the other threads scheduled?

That all depends, right?

If you have pre-emptive M:N threading on a uniprocessor then you still
have kernel scheduler support to kick off one of the other N lwp's,
right?

If you have true 1:N threading, on either UP or MP, then you only have a
user-land scheduler for your threads and then you need some kind of
asynchronous interrupt to unblock the process -- i.e. kick it with a
signal of some kind so that the blocked call returns EINTR.  Then on
seeing the EINTR your code can decide whether or not it should yield for
a bit and let some other thread get some work done.  In an ideal world
the next time the thread that was blocked gets to run then the system
call it wants to make can be called again and it won't block this time
because the kernel will have finished whatever operation was causing it
to block and the call will return right away.

So with a truly non-preemptive thread support library (i.e. N:1 threads
to processes), though that means you need to wrap blocking calls with
your own timeout mechanisms so that the blocked thread can yield to
other threads if necessary.

Pth already does that for you though though, provided you're willing to
fully use the native Pth API, and providing you can re-compile your
whole application with "pth.h".  From the Pth web page:

    The event facility allows threads to wait until various types of
    events occur, including pending I/O on filedescriptors, asynchronous
    signals, elapsed timers, pending I/O on message ports, thread and
    process termination, and even customized callback functions.

I really prefer using Pth for most threaded applications whenever
possible, especially on uniprocessor systems.  It's inherently much
safer to run most thread-using code without preemptive concurrency if at
all possible.

With Pth's wrappers for the POSIX API it's not really all that hard to
use either, or so I've been told.  I found in days gone by that most
pkgsrc applications I used that required threads were much more stable
if they could be made to work with Pth.  Some applications have improved
their saftey with true thread concurrency since then, but with so many
requiring so many huge third party libraries it's still a crapshoot.

I think if I were you Bucky I'd be at least trying to build my
application with Pth to see if it will work, and if so how well it will
work (and if not why it won't and what would be needed to make it work).
For now, while still running on a UP platform, it would seem you have
nothing to lose and everything to gain.

Hmmm.... I wonder if including "pth.h" in all of libc (and similar
system libraries) and compiling it with the supported "soft" wrappers
would help intensely threaded applications avoid even more blocking?

-- 
						Greg A. Woods

H:+1 416 218-0098 W:+1 416 489-5852 x122 VE3TCP RoboHack <woods@robohack.ca>
Planix, Inc. <woods@planix.com>       Secrets of the Weird <woods@weird.com>