[comp.os.mach] technique used to implement mutex_lock

dennisg@kgw2.bwi.WEC.COM (Dennis Glatting) (03/17/90)

how is mutex_lock() implemented in the NeXT computer?

i have several tasks with several threads each.  i had a thread lock a
mutex with mutex_lock() then, unfortunatly, died.  the mutex stayed locked.
i noticed that the NeXT was running a bit slow so i fired up gdb of
the task in question.  i found more than one thread in cthread_yield().
my question is:  when you call mutex_lock() and the mutex is locked then
what happens?  does the thread do a cthread_yield() until the mutex
is unlocked?  if that is true then why?  it seems a waste of cpu.

--
 dennisg@kgw2.bwi.WEC.COM   | Dennis P. Glatting
 ..!uunet!tron!kgw2!dennisg |

mbj@spice.cs.cmu.edu (Michael Jones) (03/18/90)

In article <429@kgw2.bwi.WEC.COM>, dennisg@kgw2.bwi.WEC.COM (Dennis Glatting) writes:
> 
> how is mutex_lock() implemented in the NeXT computer?
> 
> i have several tasks with several threads each.  i had a thread lock a
> mutex with mutex_lock() then, unfortunatly, died.  the mutex stayed locked.
> i noticed that the NeXT was running a bit slow so i fired up gdb of
> the task in question.  i found more than one thread in cthread_yield().
> my question is:  when you call mutex_lock() and the mutex is locked then
> what happens?  does the thread do a cthread_yield() until the mutex
> is unlocked?  if that is true then why?  it seems a waste of cpu.

mutex_lock(m) spins a few times calling mutex_try_lock(m), and then spins
alternating between mutex_try_lock(m) and cthread_yield().  mutex_try_lock(m)
is a test-and-set on the mutex.

Now to answer your real question.  No, it's not a waste of cpu to spin on a
mutex in a correctly written threads application.   A mutex provides mutual
exclusion between two threads.  It's intended to be called only when the
mutual exclusion is of a short or bounded duration.  It's not the right
primitive for unbounded waiting.  Any potentially unbounded waiting should be
done using condition_wait and condition_{signal,broadcast} (which don't spin).

If a thread dies, none of the memory resources which it logically owns will be
cleaned up.  That includes its locks.  If a thread dies, you have an incorrect
program anyway, and the fact that some locks are still held is the least of
your problems.

				-- Mike

jwag@moose.sgi.com (Chris Wagner) (03/22/90)

In article <8460@pt.cs.cmu.edu>, mbj@spice.cs.cmu.edu (Michael Jones) writes:
> In article <429@kgw2.bwi.WEC.COM>, dennisg@kgw2.bwi.WEC.COM (Dennis
Glatting) writes:
> > 
> > how is mutex_lock() implemented in the NeXT computer?
> > 
> > i have several tasks with several threads each.  i had a thread lock a
> > mutex with mutex_lock() then, unfortunatly, died.  the mutex stayed locked.
> > i noticed that the NeXT was running a bit slow so i fired up gdb of
> > the task in question.  i found more than one thread in cthread_yield().
> > my question is:  when you call mutex_lock() and the mutex is locked then
> > what happens?  does the thread do a cthread_yield() until the mutex
> > is unlocked?  if that is true then why?  it seems a waste of cpu.
> 
> mutex_lock(m) spins a few times calling mutex_try_lock(m), and then spins
> alternating between mutex_try_lock(m) and cthread_yield().  mutex_try_lock(m)
> is a test-and-set on the mutex.
> 
> Now to answer your real question.  No, it's not a waste of cpu to spin on a
> mutex in a correctly written threads application.   A mutex provides mutual
> exclusion between two threads.  It's intended to be called only when the
> mutual exclusion is of a short or bounded duration.  It's not the right
> primitive for unbounded waiting.  Any potentially unbounded waiting should be
> done using condition_wait and condition_{signal,broadcast} (which
don't spin).
> 
> If a thread dies, none of the memory resources which it logically owns
will be
> cleaned up.  That includes its locks.  If a thread dies, you have an
incorrect
> program anyway, and the fact that some locks are still held is the least of
> your problems.
> 
> 				-- Mike
Although in many cases, spinning is a good idea - what happens when a
thread holds
a lock for what it thnks is a short time - however during the few instructions
it has the lock it gets context switched, other threads trying to get
the lcok will spin
a while then yield. Thats ok as long the the number of threads is not too much
greater than the number of processors - but what happens when you have
100 or more
threads going after a single lock often, the time till the thread that
owns the lock
gets to run could be fairly long.


Chris Wagner

mbj@spice.cs.cmu.edu (Michael Jones) (03/23/90)

In article <5612@odin.corp.sgi.com>, jwag@moose.sgi.com (Chris Wagner) writes:
> In article <8460@pt.cs.cmu.edu>, mbj@spice.cs.cmu.edu (Michael Jones) writes:
> > Now to answer your real question.  No, it's not a waste of cpu to spin on a
> > mutex in a correctly written threads application.   A mutex provides mutual
> > exclusion between two threads.  It's intended to be called only when the
> > mutual exclusion is of a short or bounded duration.  It's not the right
> > primitive for unbounded waiting.  Any potentially unbounded waiting should
> > be done using condition_wait and condition_{signal,broadcast} (which
> > don't spin).
> 
> Although in many cases, spinning is a good idea - what happens when a
> thread holds a lock for what it thnks is a short time - however during the
> few instructions it has the lock it gets context switched, other threads
> trying to get the lcok will spin a while then yield. Thats ok as long the
> the number of threads is not too much greater than the number of processors
> - but what happens when you have 100 or more threads going after a single
> lock often, the time till the thread that owns the lock gets to run could
> be fairly long.

The case you describe can certainly happen although it should be quite rare.
A timeslice should be substantially larger than the window during which you
hold a mutex, so this should happen only with probability
(mutex_time)/(slice_time).  (And in actuality, it probably happens far less
than that for the following reason -- mutexes will often be acquired following
a condition_wait; if the condition_wait blocked, the mutex will be acquired at
the beginning of a timeslice.)  Even if it does happen, the cthread_yield() in
the spin loop of mutex_lock() will yield the processor to another thread
within a few dozen (application) instructions, allowing other threads to have
their turn.

That having been said, if you do have a parallel program with 100 threads
contending frequently for a single resource you'll probably incur
synchronization delays no matter what algorithm you use.  Restructuring your
algorithm to avoid this bottleneck might be appropriate.  Profiling your
should help determine if you have a contention bottleneck.  If you have a
potential bottleneck you can at least reduce wasted cycles by protecting this
resource with a condition variable rather than a mutex if contention is
statisticly probably, rather than statisticly unlikely (the case for which
mutexes are appropriate).

				-- Mike