Subject: Re: Threading problems
To: Nathan J. Williams <nathanw@wasabisystems.com>
From: Eric Haszlakiewicz <erh@nimenees.com>
List: tech-kern
Date: 11/23/2004 15:54:43
On Tue, Nov 23, 2004 at 04:17:02PM -0500, Nathan J. Williams wrote:
> Eric Haszlakiewicz <erh@nimenees.com> writes:
>
> > So why don't we initialize them then? I can see how we would want to
> > avoid taking the performance hit on locking for programs that don't need
> > it, but wouldn't the impact of initialization be much smaller?
>
> Initialization is something of a red herring; static initialization of
> mutexes and condvars does happen without libpthread linked in.
>
> The bigger problem is that you don't know what state things are
> supposed to be in. Consider a sequence (which I've seen) that is
> effectively like:
>
> mutex_lock()
>
> dlopen("libpthread.so")
>
> mutex_unlock()
>
> For this to work at all, the dlopen() has to cause the mutex routines
> to bind to the real thing, which itself will take some ld.so
> trickiness we don't have yet (or another layer of indirection in libc,
> which slows down both threaded and nonthreaded programs). But once
> that's working, the mutex_unlock() will be unlocking something that
> was operated on by a dummy mutex_lock(). There's no way that the code
> can magically know which bits of memory are suddenly supposed to
> become locked mutexes.
so mutex_lock() would actually have to lock the mutex, just not in
a thread safe manner. Which if it doesn't have to be thread safe is
just setting a variable, right?
So, current no-pthread mutex_lock():
checks value of global int (__isthreaded)
non-thread safe way:
possible code {
if (mutex->ptm_lock != __SIMPLELOCK_UNLOCKED)
do error
mutex->ptm_lock = __SIMPLELOCK_LOCKED;
return 1;
}
deferences pointer (the pthread_mutex_t * passed in ) (twice)
check value of int (mutex->ptm_lock) (*)
set value of int (mutex->ptm_lock) (*)
thread safe way: (no contention)
deferences pointer (the pthread_mutex_t * passed in ) (once)
call function (pthread__simple_lock_try()) (mutex->ptm_lock) (*)
set lock
deferences pointer (pointer to mutex->ptm_lock) (twice)
check value of int (*)
set value of int (*)
unset lock
set pthread_t (ptm_owner)
The thread safe way will clearly be a lot slower than the current no-pthread
way. For this particular example, I think the non-thread safe way would
allow for the thread safe way to be switched in at any time. The non-thread
safe way would be a little slower than the current way. I'm assuming
the other methods can be partially stubbed out in a similar fashion.
So, the question is: for programs that use dlopen(), is the (smaller)
performance hit a worthwhile tradeoff for allowing threaded libraries to
be seamlessly loaded? I think it probably is.
For someone that is really concerned about performance, ld.elf_so
could look at (e.g.) a LD_NOPTHREAD env variable and use the current
empty stub even if dlopen() is used.
Of course the quick fix is to have ld.elf_so always load libpthread
if dlopen() is used.
eric