+---------- 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]]

Reply via email to