+---------- On Feb 22, Andrew Piskorski said:
> In the 'ns_cond wait' command:
> http://www.aolserver.com/docs/devel/tcl/tcl-api.adp#ns_cond
>
> What is the mutex lock FOR? Do I need to use one mutex per
> cond/event, or one mutex per thread per event, or what?
>
> The docs - "The lockid argument is the id of a mutex lock." - are
> notably unhelpful, and a quick look at the C implementation didn't
> really tell me just how that mutex lock is used.
Let's use this as an example: thread T1's computation must wait for some
flag to be set. Maybe the flag is already set, in which case T1 can
proceed; maybe the flag is not set, in which case T1 must wait for some
other thread T2 to set the flag.
Now suppose that T1 could just call "ns_cond wait $cond", with no mutex.
Here's a scenario:
T1 checks the flag
- flag not set
T2 sets the flag
T2 calls ns_cond broadcast $cond
T1 calls ns_cond wait $cond
T1 hangs indefinitely, but
the flag is set!
In other words, there's an interval between T1 checking the flag and T1
calling ns_cond wait, during which T2 might set the flag and signal the
condition. If that happens, T1 misses the signal and never sees that
the flag is set.
You need T1 and T2 to cooperate using mutex to eliminate the interval:
T1 locks $mutex
T1 checks the flag
- flag not set
T2 tries to lock $mutex
- blocks because T1 owns
the lock
T1 calls ns_cond wait $cond $mutex
- this atomically unlocks $mutex
and starts waiting for $cond
to be signalled
T2 unblocks and locks $mutex
T2 sets the flag
T2 calls ns_cond broadcast $cond
T1 wakes up, still in
ns_cond wait, which tries
to lock $mutex again and
blocks because T2 still
owns the lock
T2 unlocks $mutex
T1 unblocks and locks $mutex
T1 checks the flag - flag set,
T1 carries on
The code in T1 should look something like this:
set mutex [nsv_get the_flag mutex]
set cond [nsv_get the_flag cond]
ns_mutex lock $mutex
# Use a while loop to account for
# some other thread getting woken
# first and unsetting the flag!
while {![nsv_get the_flag value]} {
ns_cond wait $cond $mutex
}
ns_mutex unlock $mutex
The code in T2 should look something like this:
set mutex [nsv_get the_flag mutex]
set cond [nsv_get the_flag cond]
ns_mutex lock $mutex
nsv_set the_flag value 1
ns_cond broadcast $cond
ns_mutex unlock $mutex
Of course you need to initialize the_flag at server startup:
nsv_array set the_flag [list \
value 0 \
mutex [ns_mutex create the_flag] \
cond [ns_cond create]]