Boost logo

Boost :

From: Matthew Hurd (matt_at_[hidden])
Date: 2004-01-25 17:28:01


> On Behalf Of Sean Kelly
> Subject: RE: [boost] Boost.Thread request: event object
>
> >> On Behalf Of Sean Kelly
> >> Subject: RE: [boost] Boost.Thread request: event object
> >> >
> >> > Do you think waiting on a mutex should also be modelled as an event?
> >>
> >> Interesting idea. My initial feeling is that it should not. While
> >> the semantics are similar, I think mutexes and events serve
> >> complimentary purposes--one is a signalling mechanism and the other a
> >> synchronization mechanism. For example, I can't imagine wanting to
> >> wait on multiple events, some of which are mutexes. And timing out
> >> waiting on a mutex seems to have different implications. Also, I
> >> can't work out a design in my head that doesn't seem confusing to use.
> >
> > Signalling and synchronisation could be the same thing.
> >
> > Multiple events, mutexes / events, could be required for any kind of
> > transaction where multiple resources are required. Makes sense to me to
> > have the same model. Transactions could involve physical resources with
> > events, such as a socket io and file io, and possibly resources that are
> > shared and thus require mutexes.
>
> Good point. I was thinking about this after I sent my last email. But
> part of me is still concerned that such a design could lead to a deadlock
> (ala the dining philosopher problem). I think if there were a time where
> I needed to acquire multiple resources for an operation I would probably
> manage access to those resources through a single object which might fire
> an event or return a handle when access was available. Even so, I'm not
> sure that this concern is enough to discount the design altogether.
> I'm kind of warming up to the idea of mutexes as events.

Deadlock can be guaranteed to be avoided if the resources are always
acquired in the same order. This could be organised by either the coder
explicitly ordering the acquisition or by a guaranteed adhoc ordering by
algorithm (by address?).

Sometimes adhoc ordering would not be good enough. You would want explicit
ordering so you acquire occasional resources up front and contentious
resources as a secondary priority to prevent holding oft needed resources
for a long time. If this was a requirement then perhaps you could have an
ordering container with adhoc acquisition if the resource wasn't in the
ordering pool.

What I call master-detail locks (I just made that up just then), where you
have a container that where a lock is acquired to pin down an item then the
item is locked and the master is then released, would still require explicit
ordering, but this is not a transaction as such and falls outside the
deadlock question I think. Note this requires that deletion and insertion
into the container do not invalidate iterators.
 
> > This makes the most sense to me as a model for sockets, file io,
> mutexes,
> > timers or other signals. On win32 other signals includes: change
> > notifications, console input, jobs, and memory resource notifications
> > beside the usual synch primitives: events, mutexes, semaphores, threads
> > and waitable timers.
>
> So how would you suggest handing the calling semantics? Would it be
> assumed that if a mutex wait operation returned true that the lock was
> acquired and false if it timed out? ie.
>
> if( mutex_event.wait( 1000 ) ) {
> ...
> mutex_event.unlock();
> }

Explicit unlocking is always bad. Too error prone for dumb schmucks like
me. Give me a chance to fail and I'll take it, maybe not every time, but
I'll find it.

You could still call a mutex event a lock. Acquire and release should
replace, or be in addition to (perhaps with deprecation), the current lock
and unlock. Existing code would be preserved.

I think "acquire" would work instead of lock or wait. Timed and try
annotations could work the same way as the current lock terminology.

I do like your idea of a conversion to bool though I worry about the
unobviousness of it. Obviousness in the confusing world of concurrency is
always good.

But it does look neat.

        if (lock lk(guard_,t))

and would release automagically as the if scope rolls out. I'm warming to
it.

Other than that you could scope it as I wrote previously:

{
        lock smock(guard_, false);
        if (smock.acquire_timed(t)) {
                // don't worry be happy
        }
}

I thought a comma operator could rescue us, but I was misguided:

        if (lock lk(guard_,false), lk.acquire_timed(t) )

would do the trick if it was valid c++, but I'm pretty sure it isn't.

Regards,

Matt Hurd.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk