Bad idea: get a read lock while holding a write lock


Under Linux, the following code hangs in the second execution of the loop at the time the write lock is acquired.

pthread_rwlock_t lock;
fprintf(stderr, « Init\n »);
pthread_rwlock_init(&lock, NULL);
for(int i = 0; i < 2; ++i) {
fprintf(stderr, « Get Write lock\n »);
pthread_rwlock_wrlock(&lock);
fprintf(stderr, « Get Read lock\n »);
pthread_rwlock_rdlock(&lock);
fprintf(stderr, « Release Read lock\n »);
pthread_rwlock_unlock(&lock);
fprintf(stderr, « Release Write lock\n »);
pthread_rwlock_unlock(&lock);
}
     pthread_rwlock_t lock;
    fprintf(stderr, "Init\n");
    pthread_rwlock_init(&lock, NULL);
    for(int i = 0; i < 2; ++i) {
        fprintf(stderr, "Get Write lock\n");
        pthread_rwlock_wrlock(&lock);
        fprintf(stderr, "Get Read lock\n");
        pthread_rwlock_rdlock(&lock);
        fprintf(stderr, "Release Read lock\n");
        pthread_rwlock_unlock(&lock);
        fprintf(stderr, "Release Write lock\n");
        pthread_rwlock_unlock(&lock);
    }

By looking at the lock state with GDB, one can see the following:

  • when the write lock is acquired, the « writer » field of the lock is initialized to some value
  • when the read lock is acquired (while holding the write lock), nothing changes
    • when acquiring a read lock without holding the write lock, the « nr_reader » field is incremented
  • when the unlock method is called (supposedly to release the read lock), the write lock is released instead (as it basically ignored the read lock)
  • when the unlock method is called again (supposedly to release the write lock), the « nr_reader » file is decremented (giving a 4292967295 = (unsigned)(-1))
  • when the write lock is reacquired in the execution of the loop, the « nr_reader » being not null, the writer is put on hold until an inexisting reader will release it…

This whole thing does not happen in Mac OS X.

A cautious man page reader would notice that on the Mac it is stated for pthread_rwlock_wrlock:
The results are undefined if the calling thread already holds the lock at the time the call is made.


Share

, , , ,

  1. #1 by Christophe on 22 mars 2010 - 18:53

    Ouais, mais fieu! Est-ce qu’t’as testé le retour de tes fonctions lock ? Parce que l’API de pthread_rwlock_rdlock, elle dit ceci:

    ERRORS

    EDEADLK
    The current thread already owns the read-write lock for writing.

    Si tu n’teste nié des valeurs de retour de fonction, ça n’pourra nié d’aller ainsi.

  2. #2 by Olivier on 23 mars 2010 - 15:43

    Je confirme. Au premier rdlock() qui suit un wrlock(), les systèmes renvoient EDEADLOCK. L’un comme l’autre considère que le lock n’a pas eu lieu car le deuxième unlock() renvoie EINVAL.

    Mais qui vérifie vraiment le résultat de pthread_*lock ? 😉

  3. #3 by Christophe on 17 mai 2010 - 21:31

    Un puriste comme moi 🙂

  4. #4 by Olivier on 17 mai 2010 - 21:47

    Toi, mon ami, je vais scanner ton code dès demain matin ! 😉

Les commentaires sont fermés.