***************************** Moderator's note: Please trim excess quoting. It wastes bandwidth and makes it difficult to find the important points among all the quoting. ***************************** William E. Kempf wrote:
Aschwin Gopalan said:
Dear Boosters! I am designing a class template with policy controled threading behaviour, like
template
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.
You are obviosly right. See my followup for the correction. I copy-pasted it out of the wrong policy class, wich I was just in progress to change to do some more testing. Sorry about that.
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
{ 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.
This looks exactly like what I need, so I just will wait for your implementation and stop bothering with my compiler problems :-) BTW, is there something regarding thread termination in the works? Actually, I am not sure if there is a safe way to do thread termination without the thread cooperating. The problem I have is that if I use blocking system calls (like read on blocking sockets) and the thread is blocking on one of those, it cannot terminate itself. At the moment, I avoid this by only using non-blocking sockets, but I would actually prefer to use blocking sockets (its just easier to get right and nicer to read).
BTW: This idea for external and internal locking is a variation on the Thread-Safe Interface design pattern suggested by Andrei Alexendrescu.
Where is it described? Is it in Moder C++ design? Thanks for the swift response, Aki