0

I'm aware boost has a support for read/writer locks however I'm wondering if it provides a class that wraps it on an object before I write my own or if there is some other way to protect a class safely.

boost read/writer https://stackoverflow.com/a/6450576/1335325

I'd like something like

class A
{
 ...
};

....

class B
{
 ....
 ReadWriterLock<A> m_a;  //Locked under read/write synchronization strategy.
};

...

how to use:

something like:

GetForRead<mutex> lock(m_a);

...
GetForWrite<mutex> lock(m_a);    

etc....

Or if someone wants to try to write on that would be great also.

Cheers

Update here is my first go at it. I would be curious as to any suggestions for improvements.

template <class T>
class WriteOnceReadMany
{
public:

    template <class T>
    class LockedRead
    {
    public:

        LockedRead(const T& data, boost::shared_mutex& mutex)
            : m_lock(mutex)
            , m_Data(&data)
        {

        }

        LockedRead(WriteOnceReadMany& data)
            : m_lock(data.m_access)
            , m_Data(&data.m_Data)
        {

        }

        operator const T&() const //Returns a copy so we cannot access the object while in use
        {
            return *m_Data;
        }

        const T& get() const  //Returns a copy so we cannot access the object while in use
        {
            return *m_Data;
        }


        const T* operator->() const
        {
            return m_Data;
        }

    private:
        boost::shared_lock<boost::shared_mutex>     m_lock;
        const T*                                    m_Data;
    };


    template <class T>
    class LockedWrite
    {
    public:

        LockedWrite(T& data, boost::shared_mutex& mutex)
            : m_lock(mutex)
            , m_Data(&data)
        {

        }


        LockedWrite(WriteOnceReadMany& data)
            : m_lock(data.m_access)
            , m_Data(&data.m_Data)
        {

        }

        LockedWrite& operator=(T const& newval)
        {
            value = newval;
            return *this;
        }

        operator T&() //Returns a copy so we cannot access the object while in use
        {
            return *m_Data;
        }

        T& get() //Returns a copy so we cannot access the object while in use
        {
            return *m_Data;
        }


        T* operator->()
        {
            return m_Data;
        }

    private:
        boost::upgrade_lock<boost::shared_mutex>    m_lock;
        T*                                          m_Data;
    };

    typedef LockedRead<T> ReadLock;
    typedef LockedWrite<T> WriteLock;

    WriteOnceReadMany(const T& data)
        : m_Data(data) 
    {

    }

    WriteOnceReadMany()
    {

    }

    ~WriteOnceReadMany()
    {
        boost::upgrade_lock<boost::shared_mutex> lock(m_access); //Make sure we can delete this object.  Potential race conditions may still occur.
    }

    WriteOnceReadMany& operator=(T const& newval)
    {
        boost::upgrade_lock<boost::shared_mutex> lock(m_access);
        m_Data = newval;
        return *this;
    }

    operator const T() const //Returns a copy so we cannot access the object while in use
    {
        boost::shared_lock<boost::shared_mutex> lock(m_access);
        return m_Data;
    }


    //These are provided to avoid copy costs.  They allow you to hold on to the object for a longer period of time.
    //Use with care and don't hold on to the lock for too long.
    ReadLock read() const
    {
        return ReadLock(m_Data, m_access);      
    }

    WriteLock write()
    {
        return WriteLock(m_Data, m_access);
    }

private:
    T                   m_Data;
    mutable boost::shared_mutex m_access;  //I really hate using mutable maybe there is a better way to keep constsness
};

Use example:

struct A
{
   int x;
   int y;
   ...
};

WriteOnceReadMany<A> m_a;

A copyOfA = m_a; //Read safe copy
m_a = copyOfA;  //Write safe operation

WriteOnceReadMany<A>::ReadLock a_readonly(m_a);  //Get as a reference for efficiency

const A& a = m_a.get(); //read only get (is const)
...

WriteOnceReadMany<A>::WriteLock a_readwrite(m_a); //Get as a reference for efficiency

A& a = m_a.get(); //write 

...
Community
  • 1
  • 1
  • 1
    I think this is generally a code smell. Locking granularity is dictated by your domain and should really never get so fine grained that you want a generic lockable-object container, IMO. But then again, consistency is a nice side-effect of using the same interface, so I'm curious what people come up with – sehe Aug 07 '14 at 22:01
  • I am curious as to why you say this is a smell (details...). It seems to me that at least in my domain I need fine grain and the encapsulation protection classes bring to prevent people accidentally accessing the object without guards. Anyway going to put my implementation up for feedback. – user1335325 Aug 07 '14 at 23:58
  • 1
    I'm not saying there cannot be exceptions, but in general if you religiously lock "per-object" then generally, concurrency will fail to scale for performance (instead generating bottlenecks around the locking). In such a case, threading refutes its own goal – sehe Aug 08 '14 at 00:00
  • The particular problem I have is the locks are too macro. So when something locks an object everyone other thread has to wait for operations to finish on that thread. By making things more granular (at the member level) I prevent this issue. The sub objects (which I writereadlock) themselves aren't accessed much but the parent objects themselves are. Anyhow I still don't get how if I was going to use a read-write lock anyway for a few of the sub member objects that have this sort of contention anyway what is the difference other than the increased protection of the objects? – user1335325 Aug 08 '14 at 00:12
  • Many locks implies more thread synchronization points, more often. You might make the locks shorter-lived, but much better is to unshare the information, and only "beam it over" in whole chunks under a single (coarser granular) lock. Just my $0.02 – sehe Aug 08 '14 at 00:51
  • Thanks for your feedback :) – user1335325 Aug 08 '14 at 19:05

0 Answers0