Boost logo

Boost :

From: Hurd, Matthew (hurdm_at_[hidden])
Date: 2004-06-29 18:45:18


> Behalf Of Batov, Vladimir
>
> > bool foo(bool threadsafe)
> > {
> > mutex::scoped_lock l(m, !threadsafe);
> > }
>
> I am not sure if the example above is appropriate and indicative of
the
> problem we are trying to solve. Thread-safety should not be a run-time
> configurable property. Like we do not decide on the fly (at run-time)
if
> our ref.-counting class is to be thread-safe or not. All that is
> resolved at compile time.

>" Thread-safety should not be a run-time configurable property."

I think this is somewhat incorrect as absolutes usually are. I
currently have a design where I use both an interface that supports
locking and an interface which does not. It is an important feature of
the design. Using such a run time parameter is not my choice because I
am especially sensitive to performance but it might be a better design
for others given other requirements.

Non-locking versions are also useful when you want to combine
operations. Take a lock, do stuff, and then release lock. Repeatedly
locking has deficiencies which may or may not matter to you.

$0.005,

Matt Hurd.

FWIW I wrap boost mutexex and locks in my own policy-like stuff. E.g:
___________________________________________________________

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

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

    struct adapted_timed_mutex : public boost::timed_rw_mutex
    {
        adapted_timed_mutex(
            lock_priority::type lp = lock_priority::exclusive
        )
        : timed_rw_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;
};
___________________________________________________________

So I can parameterise classes with a synch policy.

(Less than ideal but I haven't got around to making it nicer yet.)

For the interface where I need to support single threaded and
multi-threaded usage of the same object ( as opposed to type ) I use
this approach...
___________________________________________________________

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 );
};
___________________________________________________________

This way I can use an object in the following manner:
___________________________________________________________

te::instrument<> i_sample;

i_sample.exchange_code<true>("hello"); // locking
{
    te::instrument<>::lock lk( i_sample.guard() );
    i_sample.exchange_code<false>("hello"); // non-locking
}

i_sample.exchange_code("hello"); // locking
std::string hello = i_sample.exchange_code(); // locking
hello = i_sample.exchange_code<false>(); // non-locking
hello = i_sample.exchange_code<true>(); // locking

hello = i_sample.exchange_code_ref(); // non-locking by nature
___________________________________________________________

I could also specify
        te::instrument< synch::no_synch > // locking is elided
        te::instrument< synch::simple > // exclusive locking
        te::instrument< synch::no_synch > // allows recursive
locking
        te::instrument< synch::shareable > // allows shared or
exclusive locks (think r/w)

The interface to the non-locking version is still valid. User must code
to shareable concept for everything to work for all concepts.

Where SYNCH_SIMPLE_ATTRIBUTE defines a bunch of simple attribute
helpers and looks like:

#define SYNCH_SIMPLE_ATTRIBUTE( VAR_NAME, VAR_TYPE )
\
 
\
public:
\
    template< bool >
\
    void
\
    VAR_NAME
\
    (
\
        boost::call_traits< VAR_TYPE >::param_type new_ ## VAR_NAME
\
    )
\
    {
\
        if ( boost::needs_lock<VAR_TYPE>::value ) {
\
            lock lk(guard_ );
\
            VAR_NAME<false>( new_ ## VAR_NAME);
\
        } else {
\
            VAR_NAME<false>( new_ ## VAR_NAME);
\
        }
\
    }
\
 
\
    void
\
    VAR_NAME
\
    (
\
        boost::call_traits< VAR_TYPE >::param_type new_ ## VAR_NAME
\
    )
\
    {
\
        VAR_NAME < true > ( new_ ## VAR_NAME );
\
    }
\
 
\
    template< >
\
    void
\
    VAR_NAME<false>
\
    (
\
        boost::call_traits<VAR_TYPE>::param_type new_ ## VAR_NAME
\
    )
\
    {
\
        VAR_NAME ## _ = new_ ## VAR_NAME;
\
    }
\
 
\
    template< bool >
\
    VAR_TYPE
\
    VAR_NAME( ) const
\
    {
\
        if ( boost::needs_lock< VAR_TYPE >::value ) {
\
            lock lk(guard_, synch::lock_status::shared );
\
            return VAR_NAME<false>( );
\
        } else {
\
            return VAR_NAME<false>( );
\
        }
\
    }
\
 
\
    VAR_TYPE
\
    VAR_NAME( ) const
\
    {
\
        return VAR_NAME<true>( );
\
    }
\
 
\
    template< >
\
    VAR_TYPE
\
    VAR_NAME <false>() const
\
    {
\
        return VAR_NAME ## _;
\
    }
\
 
\
    boost::call_traits<VAR_TYPE>::const_reference
\
    VAR_NAME ## _ref( ) const
\
    {
\
        return VAR_NAME ## _;
\
    }
\
 
\
    private:
\
        VAR_TYPE VAR_NAME ## _;
\

SYNCH_SIMPLE_ATTRIBUTE includes an extra thing
        boost::needs_lock<VAR_TYPE>::value
that checks if the size of the type is > pointer size which means, for
Intel & AMD, that locking is not required as the assignment of aligned
types is atomic.

Copy constructors, assignment and cloning are further issues ;-)

IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.


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