Continuing our C++ programming tricks, we are gonna see a method for implementing smart monitors using C++ templates.
Probably you are thinking – what the hell is a smart monitor? Well, I haven’t found a better name. With “smart monitor” I mean a monitor which may be used to protected any object from concurrent access in multithreading programs. With “any object” I mean objects of any class, either written by you or implemented by third-parties. It sounds interesting, isn’t it?
Here we have a fragment of code typically problematic:
class IntGenerator {
public:
virtual int generateValue() const = 0;
};
typedef std::set<int> IntSet;
IntGenerator *igen = ...; // initializes a integer generator
IntSet dataset;
void *entrypoint(void *data) {
while (true)
dataset.insert(igen->generateValue());
}
int main() {
pthread_t thr[2];
pthread_create(&thr[0], NULL, entrypoint, NULL);
pthread_create(&thr[1], NULL, entrypoint, NULL);
}
The concurrent access to dataset probably derive in a inconsistent state in its internal binary tree.
The semantic we want to obtain is the following. We have a template class called Monitor, and we may use it in the following manner (only different code is shown).
IntSet dataset;
Monitor<IntSet> mdataset(&dataset);
void *entrypoint(void *data) {
while (true)
mdataset->insert(igen->generateValue());
}
The main difference is that we have created a new monitor object using dataset as input. Now we can use it with member by pointer operator to invoke the insert() operation. The result is that this invocation is thread-safe, i.e. there is no other thread accesing any member of dataset while insert() is invoked.
Where is the magic? Ok, obviously the monitor maintains a reference to the monitored object, used to invoke the insert() operation. The following code fragment is easily suspected.
template <class T>
class Monitor {
public:
Monitor(T *obj) : mObj(obj) {}
private:
T *mObj;
};
The presence of member by pointer operator reveals its overloading.
T *operator -> () { ... }
What’s inside that operator? Ok, not so hasty. Since this operation provides a thread-safe semantic, the following code is not possible.
T *operator -> () { return mObj;}
It’s obvious that the following also doesn’t work.
T *operator -> () {
somemutex.lock();
return mObj;
somemutex.unlock();
}
It’s very clear that any prefix may be included before the monitored object is returned. But, we can’t include any suffix after the return statement. I swear that there is a solution which provides the semantic discussed above. Would you know which one?


