Re: You provide gun, I provide foot.
do have models. What prevents you from using say, the boost::mutex::scoped_lock model? (Which has the lock/unlock calls.)
Perhaps my initial mailing was too general. I'll try to state my problem more specifically. The following url, http://www.boost.org/libs/thread/example/mutex.cpp , gives an example of how mutexes and scoped_locks are typically used. The counter class shows how an atomic counter is implemented. For completeness I'll copy and paste part of the example into my email, here's the class: class counter { public: counter() : count(0) { } int increment() { boost::mutex::scoped_lock scoped_lock(mutex); return ++count; } private: boost::mutex mutex; int count; }; As I understand it, a thread calling the 'increment' method will be blocked until any other thread that has already called 'increment' has returned from the method call (more or less). When the scoped_lock object is instantiated its constructor locks the mutex until the scoped_lock goes out of scope (at which time the destructor of scoped_lock unlocks the mutex enabling other threads to reach the body of increment). This is all fine and dandy. My problem is that I have an existing COM component which is more similar to: // please excuse the MSFT parlance class counter { public: counter() : count(0) { mutex=CreateMutex(NULL, false, NULL); } void lock() { WaitForSingleObject(mutex, INFINITE); } int increment() { return ++count; } void unlock { ReleaseMutex(mutex); } ~counter() { CloseHandle(mutex); } private: unsigned long mutex; int count; }; When using the COM component the caller is required to call 'lock' before calling any other methods (in this case 'increment'), when you are finished using the component you must call 'unlock'. So... here's my problem, how can I use Boost.Threads to create the same kind of functionality without changing my COM interfaces? Remember that this is just for illustration so my sample code may need changes before compiling ;-) In any case, the actual component is more sophisticated than the simple example I gave, and could actually be called from a language other than C++ (e.g. Visual Basic). AFAIK boost mutexes must be locked using scoped_locks and it would seem that they aren't appropriate for my situation. I can certainly appreciate the benefits of scoped_locks (esp. in a homogeneous environment where all code is written in C++). It just seems silly to force the user to use ONLY scoped_locks when in some situations the additional requirements they have make them unusable. Sure, most of the time the mutex should be unlocked at the end of the block... however when the critical section is composed of code written in more than one language or in a more dynamic algorithm it may easier to unlock it elsewhere, no? Perhaps this is an issue more appropriate for the boost-devel list... but, if there really isn't a way to simply lock/unlock a mutex I think it is awefully rude and arrogant of the developers considering the fact that more straight-forward multi-threaded IPC mechanisms have been around for quite some time and programmers have been able to use them effectively without much trouble. It's my opinion that a great toolkit not only provides robust and sophisticated facilities but *ALSO* gives the developer enough 'rope to hang themselves'. After all, isn't the freedom to shoot yourself in the foot one of the major advantages of writing code in C/C++? I use the Boost library not only to add functionality to my software but to act as a common API in cross-platform environments. Nearly every modern API supports some kind of fundamental lock/unlock mechanism for mutexes, why shouldn't boost? And yes, to answer one of the replys... I am not being forced to use boost, in this case it seems as though I can't! It would be nice however, since I'm using other Boost modules in the project. In summary, since "Lock concepts are the only way to lock and unlock a mutex object: lock and unlock functions are not exposed by any Boost.Threads mutex objects", it seems as though I'm left out in the cold! Any suggestions? Any boost developers reading this? Anyone feel my pain? Am I just being blindly stubborn? LOL, Dino
C. Michailidis wrote:
do have models. What prevents you from using say, the boost::mutex::scoped_lock model? (Which has the lock/unlock calls.)
Perhaps my initial mailing was too general.
It was ;-)
I'll try to state my problem more specifically. The following url, http://www.boost.org/libs/thread/example/mutex.cpp , gives an example of how mutexes and scoped_locks are typically used. The counter class shows how an atomic counter is implemented. For completeness I'll copy and paste part of the example into my email, here's the class:
class counter { public: counter() : count(0) { }
int increment() { boost::mutex::scoped_lock scoped_lock(mutex); return ++count; }
private: boost::mutex mutex; int count; };
As I understand it, a thread calling the 'increment' method will be blocked until any other thread that has already called 'increment' has returned from the method call (more or less). When the scoped_lock object is instantiated its constructor locks the mutex until the scoped_lock goes out of scope (at which time the destructor of scoped_lock unlocks the mutex enabling other threads to reach the body of increment).
Your understanding is correct, as far as the above code goes.
This is all fine and dandy. My problem is that I have an existing COM component which is more similar to:
// please excuse the MSFT parlance class counter { public: counter() : count(0) { mutex=CreateMutex(NULL, false, NULL); } void lock() { WaitForSingleObject(mutex, INFINITE); } int increment() { return ++count; } void unlock { ReleaseMutex(mutex); } ~counter() { CloseHandle(mutex); }
private: unsigned long mutex; int count; };
When using the COM component the caller is required to call 'lock' before calling any other methods (in this case 'increment'), when you are finished using the component you must call 'unlock'.
So... here's my problem, how can I use Boost.Threads to create the same kind of functionality without changing my COM interfaces? Remember that this is just for illustration so my sample code may need changes before compiling ;-)
Answer.. Don't take the examples at their face value ;-) Concretely you can do this: class counter { public: counter() : guard(this->mutex) { this->count = 0; this->guard.unlock(); } void lock() { guard.lock(); } int increment() { assert(this->guard.locked()); return ++count; } void unlock { assert(!this->guard.locked()); this->guard.unlock(); } // ..Nothing to do here.. // ~counter() { // } private: boost::mutex mutex; boost::mutex::scoped_lock guard; int count; }; But.. The asserts above should give you a hint of why this usage is *Not a Good Thing* (tm) normally. Of course there's nothing that prevents you from still going the safe route and doing this: class counter { public: counter() : count(0) { } void lock() { // nothing, methods act as self monitors } int increment() { boost::mutex::scoped_lock guard(this->mutex); return ++count; } void unlock { // nothing, methods act as self monitors } // ..Nothing to do here.. // ~counter() { // } private: boost::mutex mutex; int count; }; After all one would hope you are following reasonable encapsulation rules.
In any case, the actual component is more sophisticated than the simple example I gave, and could actually be called from a language other than C++ (e.g. Visual Basic). AFAIK boost mutexes must be locked using scoped_locks and it would seem that they aren't appropriate for my situation. I can certainly appreciate the benefits of scoped_locks (esp. in a homogeneous environment where all code is written in C++). It just seems silly to force the user to use ONLY scoped_locks when in some situations the additional requirements they have make them unusable. Sure, most of the time the mutex should be unlocked at the end of the block... however when the critical section is composed of code written in more than one language or in a more dynamic algorithm it may easier to unlock it elsewhere, no?
No real argument from me, and probably others, it's just a different point of view. Boost is mainly about standard C++. If we started designing with every possible outside use of code nothing would ever get done.
It's my opinion that a great toolkit not only provides robust and sophisticated facilities but *ALSO* gives the developer enough 'rope to hang themselves'. After all, isn't the freedom to shoot yourself in the foot one of the major advantages of writing code in C/C++?
With enough creativity, you can shoot yourself in the foot an infinite number of ways. But C++ is also about reducing the number of ways you can do that so that you only shoot when you really mean to shoot. After all you really want that shot to go between your toes, not through your bones. [...]
Any suggestions?
Think differently.
Any boost developers reading this?
Usually.
Anyone feel my pain?
We wouldn't be creating Boost libraries if we didn't.
Am I just being blindly stubborn?
Beauty is in the eye of the beholder. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - Grafik/jabber.org
Rene Rivera wrote:
C. Michailidis wrote:
// please excuse the MSFT parlance class counter { public: counter() : count(0) { mutex=CreateMutex(NULL, false, NULL); } void lock() { WaitForSingleObject(mutex, INFINITE); } int increment() { return ++count; } void unlock { ReleaseMutex(mutex); } ~counter() { CloseHandle(mutex); }
private: unsigned long mutex; int count; };
When using the COM component the caller is required to call 'lock' before calling any other methods (in this case 'increment'), when you are finished using the component you must call 'unlock'.
So... here's my problem, how can I use Boost.Threads to create the same kind of functionality without changing my COM interfaces?
See below, but it's not pretty. [...]
Remember that this is just for illustration so my sample code may need changes before compiling ;-)
Answer.. Don't take the examples at their face value ;-) Concretely you can do this:
class counter { public: counter() : guard(this->mutex) { this->count = 0; this->guard.unlock(); } void lock() { guard.lock(); } int increment() { assert(this->guard.locked()); return ++count; } void unlock { assert(!this->guard.locked()); this->guard.unlock(); } // ..Nothing to do here.. // ~counter() { // }
private: boost::mutex mutex; boost::mutex::scoped_lock guard; int count; };
This is not equivalent to the above scenario. You can't have two threads calling lock() at the same time, because a scoped_lock object (as currently specified) is not thread safe (whereas a mutex is, by its very nature.) [...]
class counter { public: counter() : count(0) { } void lock() { // nothing, methods act as self monitors } int increment() { boost::mutex::scoped_lock guard(this->mutex); return ++count; } void unlock { // nothing, methods act as self monitors } // ..Nothing to do here.. // ~counter() { // }
private: boost::mutex mutex; int count; };
This is also not quite equivalent to the original. In it, a lock() op1()
op2() unlock() is a transaction; no other thread can inject its own op3
between op1 and op2.
The closest you can get to the original, while somewhat respecting the
requirement for it to be callable from outside C++, is:
scoped_lock * lock()
{
return new scoped_lock( mutex );
}
void unlock( scoped_lock * lock )
{
delete lock;
}
In a C++ only program, lock() should return an auto_ptr
Peter Dimov wrote:
Rene Rivera wrote: [...] This is not equivalent to the above scenario. You can't have two threads calling lock() at the same time, because a scoped_lock object (as currently specified) is not thread safe (whereas a mutex is, by its very nature.) [...] This is also not quite equivalent to the original. In it, a lock() op1() op2() unlock() is a transaction; no other thread can inject its own op3 between op1 and op2.
Just goes to show how easy it is to shoot the foot off :-) -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - Grafik/jabber.org
participants (3)
-
C. Michailidis
-
Peter Dimov
-
Rene Rivera