Boost logo

Boost Users :

From: William E. Kempf (wekempf_at_[hidden])
Date: 2003-05-23 11:13:37


Aschwin Gopalan said:
> Dear Boosters!
> I am designing a class template with policy controled threading
> behaviour, like
>
> template<ThreadingModel=SingleThreaded>
> class Something;
>
> where there are different ThreadingModels available (see Modern C++
> Design by Alexandrescu for a discussion of policies).
>
> I would like to have Something to inherit ThreadingModel. The idea is
> that SingleThreaded does not impose any additional penalties. So my Idea
> was to have something like
>
> class SingleThreaded
> {
> public:
> void lock() {}
> void release() {}
> }
>
> class MultiThreaded
> {
> public:
> void lock() { lock_mutex(m_mutex) }
> void release() { release_mutex(m_mutex) }
> private:
> mutex m_mutex;
> }
> and have all functions call lock() and release() accordingly.
>
> But this is not good, since it does not use Resource Initialization is
> Aquisition and leads to exeption safety problems and so on. Furthermore,
> it is not (because of these reasons) possible to model using
> boost::mutex Žes since they do not expose a lock() release() interface
> but can only be locked using scoped_locks.

Sure it is, and this is what will be supplied in the next release. You
have to create a "null_mutex" type (I need a better name) that has
scoped_lock's that do nothing. This is similar to what you have below,
but applied to a new mutex type instead of your policy base type.

> So another idea is to have something like
>
> class SingleThreaded
> {
> class scoped_lock
> {
> // Empty class
> }
> }
>
> class MultiThreaded
> {
> public:
> class scoped_lock
> {
> scoped_lock() // constructor that locks m_mutex of
> // of MultiThreaded
> ~scoped_lock() // unlocks
> }
> private:
> boost::mutex m_mutex;
> }
>
> and have the member functions lock the mutex by instantiating
> an embeded scoped_lock object.
>
> Now there are some questions I have:
> 1.) Does this approach sound sensible, or am I overlooking a commonly
> used Idiom to achieve this functionality
> 2.) How can I access the m_mutex member of the right object in
> scoped_lock()? The only way I can imagin is to give the this
> pointer to the constructor (like scoped_lock lock(this);). It is
> somehow not nice, but possible

Why is this "not nice"? The syntax is similar to current ScopedLock types.

> 3.) The actual code I am using looks like
>
> //! Policy class.
> /*! Multithreading, object operations are synchronized via
> * a non-recursive lock, obeying strict Resource Acuisition is
> Initialization
> * semantics
> */
> class LockDefault
> {
> public:
> LockDefault() :
> m_pmutex(new boost::mutex)
> {
> }
> class scoped_lock : public boost::mutex::scoped_lock
> {
> public:
> scoped_lock(LockDefault &obj) :
> boost::mutex::scoped_lock(obj->m_pmutex)
> { }
> ~scoped_lock() { }
> };
>
> public:
> boost::mutex m_mutex;
>
> };
>
> This works with gcc and I think it does what it should. It
> fails to compile with VC++6 giving
>
> error C2512: 'scoped_lock<class boost::recursive_mutex>' : no
> appropriate default constructor
>
> which I do not understand, since the default constructor is never
> called.

I can't explain it, mostly because the code you posted can't be what
you're really using. You reference an m_pmutex when the only member is
m_mutex and is not the pointer type the construction would indicate it
should be.

> Also, I do not like to have the mutex member public, but if I make it
> private and write
> friend class scoped_lock;
> this does not have the desired effect (MSVC still complains about
> private data member access)

Not sure I have an answer here for the same reason.

> Somehow, I must be getting something completely wrong, so
> if somebody could point me in the right direction, I would be thankful.
>
> BTW: I am using boost_1_30_0

I'm working on something similar for the next release. Here's a synopsis
of the idea:

class foo : public boost::syncrhonized<foo, boost::mutex>
{
public:
   void bar() { scoped_lock lock(this); bar(lock); }
   void bar(const scoped_lock& lock) {
      // do stuff
   }
   void baz() { scoped_lock lock(this); baz(lock); }
   void baz(const scoped_lock& lock) {
      // do stuff
   }
};

foo f;
// Let the class do the synchronization
f.bar();
// Do the synchronization ourselves, for the right granularity
{ // Artificial block for lock
   foo::scoped_lock lock(f);
   f.bar(lock);
   f.baz(lock);
} // Done with lock

There's some implementation problems with this design that have to be
worked out yet (for instance, we need a base class for ScopedLocks so that
the above public methods for external synchronization don't have to be
tied to specific lock types). I should have this implemented sometime
this next week, though, so I'll keep you informed of progress.

BTW: This idea for external and internal locking is a variation on the
Thread-Safe Interface design pattern suggested by Andrei Alexendrescu.

-- 
William E. Kempf

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net