Boost logo

Boost :

From: Matt Hurd (matt.hurd_at_[hidden])
Date: 2005-04-28 13:50:14


On 4/29/05, Michael Glassford <glassfordm_at_[hidden]> wrote:
> I've been mentioning that I have some ideas about improvements for
> Boost.Threads. I intend to bring up these ideas one at a time for
> discussion. I'm particularly interested in finding out:

Kewl.
 
> 1) Whether people think that the idea would be an improvement.
> 2) If so, suggestions for improving the idea even more.
>
> The first idea has to do with the long discussion that occured some time
> back about the lock class--in particular, its constructors. There were
> several ideas about whether lock constructors should make the full range
> of lock, try_lock, and timed_lock functionality available and, if so,
> how; I don't recall any consensus being reached. I experimented for a
> while with a series of template classes that would allow a user to
> choose whatever interface they wanted, but another idea has occured to
> me since then that I don't remember being mentioned anywhere (if I'm
> wrong, my apologies to whoever mentioned it): to eliminate the locking
> contructors altogether. In this scheme, a lock object would no longer be
> responsible for locking a mutex, only for unlocking it. Instead, a mutex
> object would lock itself and transfer ownership of the lock to a lock
> object by way of a lock_transfer object.
>
> A sample of what the classes would look like and their usage is given at
> the end of this message.
>
> Some advantages I see to this approach:
>
> #1: it simplifies the lock class and allows it to be used to lock any
> costructor, no matter what its type (it can own a lock to an exclusive
> mutex, an exclusive lock to a shared_mutex, or a shared lock to a
> shared_mutex).
>
> #2: instead of the following, which in my mind leaves an ambiguity of
> whether the mutex should be unlocked, shared-locked, or exclusive-locked
> at the end:
>
> shared_lock l1(shared_mutex, SHARED_LOCK);
> ...
> {
> exclusive_lock l2 = l1.promote();
> ...
> }
>
> It is possible to write this, which removes the ambiguity:
>
> lock l = shared_mutex.shared_lock();
> ...
> l = shared_mutex.promote(l.transfer());
> ...
>
> Comments?
>
> Mike
>

Bad example I think. Personally, the risk of dead lock from trying to
promote shared to an exclusive lock strikes me as goto-esque. I
really struggle to find a compelling use case that makes the danger
involved worthwhile. I recall suggesting that if the dangerous
efficiency was really required it should be through a special
promotable shareable mutex rather than the normal shareable one.

I agree with your push to clean up the lock constructor interface. I
currently wrap them all and use my own interface so I can have generic
code for null, exclusive and shared ops.

Not sure about the lock transfer object as it is really just another
name for a lock. After all it is the token representing the locked
state, or a lock by another name... Perhaps just being able to return
a lock from a mutex and being able to transfer ownership by copying a
lock like an auto ptr would give you the same thing. I need to think
about this some more.

Importantly, I believe you need to be able to request a share lock
even of an exclusive mutex or null mutex so that you can write generic
concurrent code. You write to a shared model and the other models
work.

In the code below, you can see I simply added an extra parameter to
all the constructor with the lock status request to make it
orthogonal. With your approach you could have a lock transfer object
being able to be constructed from a shared_lock, exclusive_lock,
method. I'd want to see all mutexes with shared and exclusive lock
methods, including the null mutex that boost doesn't have, but I'm
not sure your suggested approach has an advantage.

matt.
_________________

I use this lock redirection:

    template<class Mutex>
    struct null_lock
    {
        null_lock( Mutex& m, lock_status::type ls = lock_status::exclusive ) {}
        bool try_lock() {}
        bool timed_lock(const boost::xtime &) {}

    private:
        null_lock (const null_lock&);
        null_lock& operator= (const null_lock&);
    };

    template<class Mutex>
    struct simple_lock
    {
        simple_lock( Mutex& m, lock_status::type ls =
lock_status::exclusive) : lock_(m) {}
        bool try_lock() { return lock_.try_lock(); }
        bool timed_lock(const boost::xtime &xt ) { return
timed_lock.timed_lock(xt); }

    private:
        simple_lock (const simple_lock&);
        simple_lock& operator= (const simple_lock&);

    private:
        typename Mutex::scoped_lock lock_;
    };

    template<class Mutex>
    struct shareable_lock
    {
        shareable_lock( Mutex& m, lock_status::type ls =
lock_status::exclusive) : lock_(m,convert(ls)) {}
        bool try_lock() { return lock_.try_lock(); }
        bool timed_lock(const boost::xtime &xt ) { return
timed_lock.timed_lock(xt); }
    private:
        shareable_lock (const shareable_lock&);
        shareable_lock& operator= (const shareable_lock&);

    private:
        typename Mutex::scoped_read_write_lock lock_;

    };

With these kind of type wrappers:

    struct no_synch
    {
        struct nil_mutex
        {
            nil_mutex( lock_priority::type lp = lock_priority::exclusive ) {}
            ~nil_mutex() {}
        };

        typedef nil_mutex mutex;
        typedef nil_mutex try_mutex;
        typedef nil_mutex timed_mutex;

        typedef null_lock<mutex> lock;
        typedef null_lock<try_mutex> try_lock;
        typedef null_lock<timed_mutex> timed_lock;
        
        typedef atomic_op_simple atomic_op;
    };

    struct simple
    {
    protected:
        struct adapted_mutex : public boost::mutex
        {
            adapted_mutex( lock_priority::type lp =
lock_priority::exclusive ) {}
            ~adapted_mutex() {}
        };

        struct adapted_try_mutex : public boost::try_mutex
        {
            adapted_try_mutex( lock_priority::type lp =
lock_priority::exclusive ) {}
            ~adapted_try_mutex() {}
        };

        struct adapted_timed_mutex : public boost::timed_mutex
        {
            adapted_timed_mutex( lock_priority::type lp =
lock_priority::exclusive ) {}
            ~adapted_timed_mutex() {}
        };

    public:
        typedef adapted_mutex mutex;
        typedef adapted_try_mutex try_mutex;
        typedef adapted_timed_mutex timed_mutex;

        typedef simple_lock<mutex> lock;
        typedef simple_lock<try_mutex> try_lock;
        typedef simple_lock<timed_mutex> timed_lock;

        typedef atomic_op_interlocked atomic_op;
    };

    struct recursive
    {
    protected:
        struct adapted_mutex : public boost::recursive_mutex
        {
            adapted_mutex( lock_priority::type lp =
lock_priority::exclusive ) {}
            ~adapted_mutex() {}
        };

        struct adapted_try_mutex : public boost::recursive_try_mutex
        {
            adapted_try_mutex( lock_priority::type lp =
lock_priority::exclusive ) {}
            ~adapted_try_mutex() {}
        };

        struct adapted_timed_mutex : public boost::recursive_timed_mutex
        {
            adapted_timed_mutex( lock_priority::type lp =
lock_priority::exclusive ) {}
            ~adapted_timed_mutex() {}
        };

    public:
        typedef adapted_mutex mutex;
        typedef adapted_try_mutex try_mutex;
        typedef adapted_timed_mutex timed_mutex;

        typedef simple_lock<mutex> lock;
        typedef simple_lock<try_mutex> try_lock;
        typedef simple_lock<timed_mutex> timed_lock;

        typedef atomic_op_interlocked atomic_op;
    };

    struct shareable
    {
    protected:
        struct adapted_mutex : public boost::read_write_mutex
        {
            adapted_mutex(
                lock_priority::type lp = lock_priority::exclusive
            )
            : read_write_mutex( convert( lp) ) {}
            ~adapted_mutex() {}
        };

        struct adapted_try_mutex : public boost::try_read_write_mutex
        {
            adapted_try_mutex(
                lock_priority::type lp = lock_priority::exclusive
            )
            : try_read_write_mutex( convert( lp) ) {}
            ~adapted_try_mutex() {}
        };

        struct adapted_timed_mutex : public boost::timed_read_write_mutex
        {
            adapted_timed_mutex(
                lock_priority::type lp = lock_priority::exclusive
            )
            : timed_read_write_mutex( convert( lp) ) {}
            ~adapted_timed_mutex() {}
        };

    public:
        typedef adapted_mutex mutex;
        typedef adapted_try_mutex try_mutex;
        typedef adapted_timed_mutex timed_mutex;

        typedef shareable_lock<mutex> lock;
        typedef shareable_lock<try_mutex> try_lock;
        typedef shareable_lock<timed_mutex> timed_lock;

        typedef atomic_op_interlocked atomic_op;

    };


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