JUL
26
2007

mutexes

If you ever did anything with multithreading you'd know mutexes. They are basically a building block to do any multithreading work in.

In java they are better known as 'synchronized blocks'. Your basic hot zone can be protected by a combination of myMutex.lock(); /* do stuff here*/ myMutex.unlock();
Which is equivalent to the Java manner of synchronized(myMutex) { /* do stuff here */ }

My biggest problem with the mutex method is that if I add a return before the unlock part, I've got a problem. The mutex will never be unlocked. So you will get weird errors that just when that corner case happens which will do such an early return, things will lock up.

So I tend to write the following:

  QMutex lock;
  {
      struct Finalizer {
        Finalizer(QMutex &lock) : l(&lock) { l->lock(); }
        ~Finalizer() { l->unlock(); }
         QMutex *l;
      };
      Finalizer finalizer(lock);
      // foo bar
  }

This means that even if I do a return; call somewhere in that "foo bar" block my lock will nicely be unlocked again.

Now; is there someone that is good with macros and allow me to type the following to expand to the former?

  QMutex lock;
  synchronized(lock) {
     // foo bar
  }

Have fun!

Comments

Why are you not using QMutexLocker?


{
QMutexLocker lock(&mutex);
...
}

And defining the mutex in front of the secion does not make sense, because each thread gets its own mutex. I hope this is only for demonstration purposes.


By ponto at Thu, 07/26/2007 - 20:49

It seems that thats a nice alternative, indeed. Though a name thats totally not in line with its function (its main purpose is unlocking IMO), which is probably why I missed it.

Though I'd still love a macro that allows me to write synchronized(lock) { } ;)

oh, yes. the lock there was just for demonstration purposes, I tend to have it as a member of a class, for example.


By Thomas Zander at Thu, 07/26/2007 - 21:10

You could try the following:


#include

#define JOIN( X, Y ) DO_JOIN( X, Y )
#define DO_JOIN( X, Y ) X##Y

class Locker {
public:
Locker(int i) : _state(true) { std::cout << "Lock" << std::endl; }

~Locker() { std::cout << "Unlock" << std::endl; }

bool & state() { return _state; }
bool _state;
};

#define synchronized(A) for (Locker JOIN(lockername, __LINE__)(A); \
JOIN(lockername, __LINE__).state(); \
JOIN(lockername, __LINE__).state() = false)

int main() {

synchronized(1) {
std::cout << "Huhu" << std::endl;
}

}

However in my opinion this is bad style.

Using RAII for this is easily recognized by C++ developers. That's why I prever the QMutexLocker object.


By ponto at Thu, 07/26/2007 - 21:38

I fail to see why the name is not in line with its function: QMutexLocker do lock the mutex itself. It also makes sure the mutex gets unlocked when you leave the scope of the locker. You do not have to manually call QMutex::lock().


By aurélien gâteau at Fri, 07/27/2007 - 08:54

> QMutexLocker do lock the mutex itself.

Sure, I never said it doesn't. If I take an airplane (the big 747 kind), its main function is to fly and its name is in line with it. The other parts like being able to sit a person are all trivial in comparison.
I feel calling the unlocker-when-out-of-scope a "locker" is similar to calling an airplane a 'personSitter'. There is no doubt it fulfills that function, but if I see that name in a list of items it surely will not pop out at me when I'm looking for a way to fly.

Or, the other way around; when I see a QMutexLocker(&lock); at the top of a function, I'm still going to spent a couple of seconds thinking why there is no matching unlock.


By Thomas Zander at Fri, 07/27/2007 - 11:35

I don't get that comparison, but it doesn't matter. You just should get used to this naming because you will find this kind of terminology in quite a few threading libs. Have a look at Boost, OpenTop or GNU Common C++ for example. Most of them call those objects Lock, ScopedLock or MutexLock.


By aurélien gâteau at Fri, 07/27/2007 - 20:29

#include "stdio.h"

// ================= Dummy ===================

struct QMutex {
int i;
void lock() { printf("lock\n"); fflush(stdout); }
void unlock() { printf("unlock\n"); fflush(stdout); }
};

// ================= Header ==================
struct Finalizer {
Finalizer(QMutex &lock) : l(&lock), b(1) { l->lock(); }
~Finalizer() { l->unlock(); }
QMutex *l;
int b;
int loop() { return b; }
void inc() { --b; }
};

#define synchronized(T) QMutex T; \
for(Finalizer finalizer(T); finalizer.loop(); finalizer.inc())

// ================ Test ======================

int main() {
synchronized(lock1) { printf("1\n"); }
synchronized(lock2) { printf("2\n"); }
return 0;
}

// ================ Enjoy =====================


By Fred P. at Fri, 07/27/2007 - 02:43

Works great, thanks :)


By Thomas Zander at Fri, 07/27/2007 - 08:42

This looks alot like Andrei Alexandrescu's ScopeGuard. See: http://erdani.org/publications/cuj-12-2000.html

This is implemented with some improvements in at least one place. For links, see http://erdani.org/publications/main.html about a 1/3 from the bottom, look for scope guard.


By fok at Fri, 07/27/2007 - 04:20