I have defined various classes to simulate
resource locking using critical-section, mutex, etc.
These classes don't really have anything in
common except they all support two methods - Lock()
and Unlock(). Anyone who is using an instance
of any of these classes will always call Lock and Unlock
in pair. For example.
CMyCriticalSection cs;
...
cs.Lock();
....
cs.Unlock();
In order to ensure that an Unlock is definitely called after
an unlock, I have defined a wrapper class that calls
these two methods in the constructor and destructor
respectively.
{
CMyCriticalSectionLock lock(cs);
....
}
However, I do not wish to define one wrapper lock class
for critical section, one for mutex, and so on.
This problem can be solved if I define a virtual base class with
two virtual methods - Lock and Unlock. However, I do not wish
to introduce the overhead of virtual functions just for what I am
trying to achieve.
Another way is to define a template based wrapper
template <class T> class CMyLock
{
CMyLock(T& r) : m_r(r)
{
m_r.Lock();
}
~CMyLock()
{
m_r.Unlock()
}
T& m_r;
}
However, the usage then becomes
CMyLock<CMyCriticalSection> lock(cs).
CMyLock<CMyMutex> lock(mutex);
and so on.
What I am hoping is to find a mechanism such that
one can simply use it as
CMyLock lock(resource)
where resource could be a critical section or a mutex or a
semaphore.
In summary, what I wish to achieve is
1. Ease of use.
2. No virtual function overhead.
Does anyone have a suggestion?
Thank you in advance for your help.
Kumar
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
I have a hint for you. It's not very elegant nor scaleable, but it's
efficient. And it's okay if you don't want to add many synchronization
objects.
The approach uses a tagged union and a template constructor. If you don't
have support for template methods, forget it. If yes, here's something that
may cut the deal:
class CMyLock
{
enum { MUTEX, SEMAPHORE, CRITICALSECTION } Kind;
union
{
CMutex *pMutex;
CSemaphore *pSemaphore;
CCriticalSection *pCriticalSection;
};
public:
template <class T> inline CMyLock(T &t);
~CMyLock();
};
template<> inline CMyLock::CMyLock(CMutex &M)
: pMutex(&M), Kind(MUTEX)
{
}
template<> inline CMyLock::CMyLock(CSemaphore &S)
: pSemaphore(&s), Kind(SEMPAHORE)
{
}
template<> inline CMyLock::CMyLock(CCriticalSection &C)
: pCriticalSection(&S), Kind(CRITICALSECTION)
{
}
CMyLock::~CMyLock()
{
// You'll hate this
switch (Kind)
{
case MUTEX:
pMutex->Unlock();
break;
case SEMAPHORE:
pSemaphore->Unlock();
break;
case CRITICALSECTION:
pCriticalSection->Unlock();
break;
default:
// Send an email to management
// with the login name of the programmer
}
}
Hope this helps,
Andrei
This solution is goung to be *less* efficient
than one with virtual function call. I know,
I measured. The difference is about 20% on my
compiler/processor.
--
Regards
Anatoli (anatoli at ptc dot com) -- opinions aren't
> I have a hint for you. It's not very elegant nor scaleable, but it's
> efficient. And it's okay if you don't want to add many synchronization
> objects.
[...]
> switch (Kind)
> {
> case MUTEX:
[...]
Really all this does is emulate virtual functions, and rather untidily
at that. As a result, it may well result in more runtime overhead than
would using virtual functions in the first place.
To the original poster: I don't see any better way around this than to
derive the various lockable classes from a common base as you have
already suggested. Have you expermented with this approach, to see
whether it makes a significant difference to _overall_ performance?
You may be pleasantly surprised.
If there _are_ noticable performance implications, you could of course
implement both types of wrapper - one using templates and one using
virtual functions. For the same of convenience, you would use the
virtual wrapper in most situations; use of the template wrapper would
be reserved for particularly performance-critical areas.
-- Mat.
> Andrei Alexandrescu <alexan...@micromodeling.com> wrote:
> > >What I am hoping is to find a mechanism such that
> > >one can simply use it as
> > > CMyLock lock(resource)
> > >where resource could be a critical section or a mutex or a
> > >semaphore.
>
> > I have a hint for you. It's not very elegant nor scaleable, but it's
> > efficient. And it's okay if you don't want to add many
synchronization
> > objects.
>
> [...]
>
> > switch (Kind)
> > {
> > case MUTEX:
>
> [...]
>
> Really all this does is emulate virtual functions, and rather untidily
> at that. As a result, it may well result in more runtime overhead than
> would using virtual functions in the first place.
>
> To the original poster: I don't see any better way around this than to
> derive the various lockable classes from a common base as you have
> already suggested. Have you expermented with this approach, to see
> whether it makes a significant difference to _overall_ performance?
> You may be pleasantly surprised.
My boss has a quote taped to his bookshelf attributed to Bill Wulf. It
goes like this:
"More computing sins are committed in the name of efficiency (without
necessarily achieving it) than for all other reasons combined
including blind stupidity."
Regards,
Jon Trauntvein
The pithy version, which may be attributed to Jon Bentley, is
"Premature optimization is the root of all evil."
>Netters,
>
>I have defined various classes to simulate
>resource locking using critical-section, mutex, etc.
>These classes don't really have anything in
>common except they all support two methods - Lock()
>and Unlock(). Anyone who is using an instance
>of any of these classes will always call Lock and Unlock
>in pair. For example.
Can you change the mutex, semaphore and Critical section classes to
have a common ancestor?
CLockingMechanisms {
CLockingMechanisms();
virtual void Lock() = 0;
virtual void Unlock() = 0;
virtual ~CLockingMechanisms() = 0;
};
This way you can construct your wrapper using the base class
Cheers,
Vivek Venugopalan
vivek(at)mcs(dot)net
That was done to prevent spammers.
Please remove the garbage in my <reply to> address before
replying back to me.
[ ... ]
> The pithy version, which may be attributed to Jon Bentley, is
> "Premature optimization is the root of all evil."
I believe Jon would object to that. Donald Knuth makes this statement
in volume 1, and gives no attribution -- given his meticulous nature,
I'd guess he originated it.
--
Later,
Jerry.
The Universe is a figment of its own imagination.