Boost logo

Boost :

From: Matt Hurd (matt.hurd_at_[hidden])
Date: 2004-07-14 18:13:15


On Wed, 14 Jul 2004 18:45:00 +0300, Peter Dimov <pdimov_at_[hidden]> wrote:
> Howard Hinnant wrote:
> > On Jul 14, 2004, at 6:27 AM, Alexander Terekhov wrote:
> >
> >> It is a philosophical issue, not technical.
> >
> > Thanks for your comments.
> >
> > Speaking philosophically ... just wondering out loud, not trying to
> > solve anything, I wonder if this doesn't boil down to an object
> > oriented view vs a functional view. Because the few times I've felt
> > the need for a recursive mutex is when I had an object, which contained
> > a single mutex, which had several public member functions that just
> > happened to call each other as an implementation detail. Something
> > like:
> >
> > class A
> > {
> > public:
> > void foo1(); // calls foo2 and foo3 under the covers
> > void foo2();
> > void foo3();
> > private:
> > mutex mut_;
> > };
>
> Yep, classic. ;-)
>
> The solution is, of course:
>
> class A
> {
> public:
>
> void foo1()
> {
> scoped_lock<> lock(mut_);
> foo1_impl();
> }
>
> void foo2()
> {
> scoped_lock<> lock(mut_);
> foo2_impl();
> }
>
> void foo3()
> {
> scoped_lock<> lock(mut_);
> foo3_impl();
> }
>
> private:
>
> void foo1_impl(); // calls foo2_impl & foo3_impl
> void foo2_impl();
> void foo3_impl();
>
> mutex mut_;
> };

Yes, that is the classic solution but it never strikes me as quite what I need.

I've been struggling to find a cute solution I'm happy with. Mainly
because I also typically want to expose the foo1_impl() for the cases
when I wish to lock externally for multiple operations.

One thing I've done is have the standard interface lock and have
template methods that take true and false for locking and non-locking.

    do_this(); // locks - safety first
    do_this<true>(); // locks
    do_this<false>(); // no locking

To do this I inherit a monitor class which exposes a guard() method
that return a mutex reference which is used for locking.

The messy part is being able to do it without lots of boilerplate code.

Presently I use a macro for getter/setter attributes which implements
the appropriate methods and also uses a compile time check for
attribute size to determine if locking is truly necessary for the
platform (some operations are atomic and need no locking, e.g. 32 bit
ops on IA32 and 64 bit ops on IA64).

This is fine. However this macro/specialisation approach falls apart
when you have an arbitrary method as I don't know a neat way to
encapsulate the passing of the parameters in the macro for
replication.

However, the named_params library does this nicely and I think I could
use that for the arbitary method approach, I just can't quite find the
time to do it.

I've previously posted this stuff but can again if there is any interest.

Regards,

Matt Hurd.
 
_______________

Here is a simple attribute only example:

namespace te {

template
<
    class S = synch::shareable
>
class instrument : public synch::monitor<S>
{

    SYNCH_SIMPLE_ATTRIBUTE( susquehanna_code, std::string );
    SYNCH_SIMPLE_ATTRIBUTE( exchange_code, std::string );
    SYNCH_SIMPLE_ATTRIBUTE( underlying, std::string );
    SYNCH_SIMPLE_ATTRIBUTE( bid, double );
    SYNCH_SIMPLE_ATTRIBUTE( bid_volume, double );
    SYNCH_SIMPLE_ATTRIBUTE( ask, double );
    SYNCH_SIMPLE_ATTRIBUTE( ask_volume, double );
    SYNCH_SIMPLE_ATTRIBUTE( last, double );
    SYNCH_SIMPLE_ATTRIBUTE( last_volume, double );
    SYNCH_SIMPLE_ATTRIBUTE( prior_settlement, double );

public:
    instrument() : synch::monitor<S>()
    {
    }

    template < class T >
    instrument( const T & t) : synch::monitor<S>()
    {
        T::lock lk( t.guard(), synch::lock_status::shared );

        susquehanna_code<false> ( t.susquehanna_code<false>() );
        exchange_code<false> ( t.exchange_code<false>() );
        underlying<false> ( t.underlying<false>() );
        bid<false> ( t.bid<false>() );
        bid_volume<false> ( t.bid_volume<false>() );
        ask<false> ( t.ask<false>() );
        ask_volume<false> ( t.ask_volume<false>() );
        last<false> ( t.last<false>() );
        last_volume<false> ( t.last_volume<false>() );
        prior_settlement<false> ( t.prior_settlement<false>() );
    }

    template< class T >
    instrument< S > & operator=( const T& rhs )
    {
        if (&rhs != this)
        {
            lock lk_this( guard(), synch::lock_status::exclusive );
            instrument<S> temp(rhs);
            swap(temp);
        }
        return *this;
    }

    template< class T>
    void swap( T& source) throw()
    {
        std::swap( susquehanna_code_, source.susquehanna_code_);
        std::swap( exchange_code_, source.exchange_code_);
        std::swap( underlying_, source.underlying_);
        std::swap( bid_, source.bid_);
        std::swap( bid_volume_, source.bid_volume_);
        std::swap( ask_, source.ask_);
        std::swap( ask_volume_, source.ask_volume_);
        std::swap( last_, source.last_);
        std::swap( last_volume_, source.last_volume_);
        std::swap( prior_settlement_, source.prior_settlement_);
    }

};

} // namespace


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