2013-12-13 11 views
7

Ich versuche, robuste Mutexe auf Linux zu verwenden, um Ressourcen zwischen Prozessen zu schützen, und es scheint, dass sie sich in manchen Situationen nicht "robust" verhalten. Mit "robust" meine ich, dass pthread_mutex_lock EOWNERDEAD zurückgeben sollte, wenn der Prozess, der die Sperre besitzt, beendet wurde. HierFehler mit robustem Mutex

ist das Szenario, in dem es nicht funktioniert:

2 Prozesse P1 und P2. p1 erstellt robusten Mutex und wartet darauf (nach Benutzereingabe). p2 hat 2 Threads: Thread 1 maps in den Mutex und erwirbt es. Thread 2 (nachdem Thread 1 den Mutex erworben hat) mappt ebenfalls in den gleichen Mutex und wartet darauf (da Thread 1 ihn jetzt besitzt). Beachten Sie auch, dass p1 auf den Mutex wartet, nachdem p2-thread1 es bereits erfasst hat.

Wenn wir jetzt p2 beenden, wird p1 niemals entsperrt (was bedeutet, dass pthread_mutex_lock niemals zurückkehrt), im Gegensatz zur angenommenen "Robustheit", wo p1 mit dem EOWNERDEAD-Fehler entsperren sollte. Hier

ist der Code:

p1.cpp:

#include <sys/types.h> 
#include <sys/mman.h> 
#include <fcntl.h> 
#include <pthread.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/time.h> 

struct MyMtx { 
    pthread_mutex_t m; 
}; 

int main(int argc, char **argv) 
{ 
    int r; 

    pthread_mutexattr_t ma; 
    pthread_mutexattr_init(&ma); 
    pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); 
    pthread_mutexattr_setrobust_np(&ma, PTHREAD_MUTEX_ROBUST_NP); 

    int fd = shm_open("/test_mtx_p", O_RDWR|O_CREAT, 0666); 
    ftruncate(fd, sizeof(MyMtx)); 

    MyMtx *m = (MyMtx *)mmap(NULL, sizeof(MyMtx), 
     PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0); 
    //close (fd); 

    pthread_mutex_init(&m->m, &ma); 

    puts("Press Enter to lock mutex"); 
    fgetc(stdin); 

    puts("locking..."); 
    r = pthread_mutex_lock(&m->m); 
    printf("pthread_mutex_lock returned %d\n", r); 

    puts("Press Enter to unlock"); 
    fgetc(stdin); 
    r = pthread_mutex_unlock(&m->m); 
    printf("pthread_mutex_unlock returned %d\n", r); 

    puts("Before pthread_mutex_destroy"); 
    r = pthread_mutex_destroy(&m->m); 
    printf("After pthread_mutex_destroy, r=%d\n", r); 

    munmap(m, sizeof(MyMtx)); 
    shm_unlink("/test_mtx_p"); 

    return 0; 
} 

p2.cpp:

#include <sys/types.h> 
#include <sys/mman.h> 
#include <fcntl.h> 
#include <pthread.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 

struct MyMtx { 
    pthread_mutex_t m; 
}; 

static void *threadFunc(void *arg) 
{ 
    int fd = shm_open("/test_mtx_p", O_RDWR|O_CREAT, 0666); 
    ftruncate(fd, sizeof(MyMtx)); 

    MyMtx *m = (MyMtx *)mmap(NULL, sizeof(MyMtx), 
     PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0); 
    sleep(2); //to let the first thread lock the mutex 
    puts("Locking from another thread"); 
    int r = 0; 
    r = pthread_mutex_lock(&m->m); 
    printf("locked from another thread r=%d\n", r); 
} 

int main(int argc, char **argv) 
{ 
    int r; 
    int fd = shm_open("/test_mtx_p", O_RDWR|O_CREAT, 0666); 
    ftruncate(fd, sizeof(MyMtx)); 

    MyMtx *m = (MyMtx *)mmap(NULL, sizeof(MyMtx), 
     PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0); 
    //close (fd); 

    pthread_t tid; 
    pthread_create(&tid, NULL, threadFunc, NULL); 

    puts("locking"); 
    r = pthread_mutex_lock(&m->m); 
    printf("pthread_mutex_lock returned %d\n", r); 

    puts("Press Enter to terminate"); 
    fgetc(stdin); 

    kill(getpid(), 9); 
    return 0; 
} 

Zuerst laufen p1, dann p2 laufen und warten, bis er druckt „Locking von einem anderen Thread ". Drücken Sie die Eingabetaste in der Shell von p1, um den Mutex zu sperren, und drücken Sie dann die Eingabetaste in der Shell von p2, um p2 zu beenden, oder Sie können ihn einfach auf andere Weise beenden. Sie werden sehen, dass p1 "locking ..." und pthread_mutex_lock niemals zurückgibt.

Das Problem passiert eigentlich nicht die ganze Zeit, sieht aus wie es vom Timing abhängt. Wenn Sie eine gewisse Zeit verstreichen lassen, nachdem p1 zu sperren beginnt und bevor Sie p2 beenden, funktioniert es irgendwann und pthread_mutex_lock von p2 gibt 130 (EOWNERDEAD) zurück. Aber wenn Sie p2 kurz nach oder kurz nachdem p1 auf den Mutex wartet, beenden, wird p1 niemals entsperren.

Hat noch jemand das gleiche Problem?

+0

Ich habe auch den Code von p2.cpp geändert, um die Zuordnung in den gemeinsamen Speicher zweimal zu vermeiden, indem MyMtx * m globale Variable und für threadFunc, um es anstelle des Aufrufs von mmap verwenden. Ich bekomme das gleiche Ergebnis. –

+0

Seltsam, wenn ich SIGTERM für Ihr SIGKILL ersetze, scheint es wie erwartet zu funktionieren. Bezieht sich die Spezifikation auf variierendes Verhalten basierend auf dem Signal? – Duck

+0

Hmm, ich habe versucht mit SIGTERM und es reproduziert immer noch. Das selbe mit Ctrl-C von p2 (was SIGINT ist, denke ich). Wenn ich p2 gleich nach dem Start von p1 beende, gibt es eigentlich immer für mich wieder. –

Antwort

0

Versuchen Sie, Ihr Problem zu vereinfachen. Es scheint, dass Ihr Problem Sequenz läuft.
Immer das schlimmste Szenario betrachten: sogar Sie laufen A, dann B, B kann immer noch beenden, während A gerade anfangen zu laufen. Fügen Sie bei Bedarf ein Mutex-Steuerelement hinzu.
Hier ist ein einfaches Beispiel für A (Produzenten) und B (Verbraucher):

 Main: 
      Call A 

     A: 
      Lock 
      Call B 
      Produce 
      Unlock 

     B: 
      Lock 
      Consume 
      Unlock 
+0

Wirst du nicht einen Deadlock haben, da du die Sperre zweimal erwirbst (es sei denn, du verwendest Reentrant-Sperren)? Für mein Beispiel weiß ich jedoch, wann und von wem die Sperre von den printfs erworben wird. In jedem Fall spielt es keine Rolle, welcher Thread in Prozess 2 die Sperre erhalten würde, gemäß der "Robustheit", wie ich es verstehe, wenn Prozess 2 stirbt, sollte Prozess 1 in der Lage sein, die Sperre zu erlangen. –

+0

Also, was Sie brauchen, ist [Pthread_cond_t] (http://www.sourceware.org/pthreads-win32/manual/pthread_cond_init.html), Sie werden das brauchen, wenn Sie Mutex verwenden. Wenn Sie ein Beispiel benötigen, kann ich einen einfachen Beitrag veröffentlichen. – moeCake

+0

Ich bin nicht sicher, wie pthread_cond hier helfen würde, da Prozess 2 beendet ist, so dass es keine Chance haben wird, den Zustand zu signalisieren. –

1

Nur verifiziert Verhalten mit glibc Version: 2.11.1 auf Linux Kernel 2.6.32 und höher.

Mein erster Befund: Wenn Sie in p1 vor dem "Sperren aus einem anderen Thread" in p2 (innerhalb von 2s) die Eingabetaste drücken, funktioniert der robuste Mutex. wie man es erwarten würde. Fazit: Die Reihenfolge der wartenden Threads ist wichtig.

Der erste wartende Thread wird aufgeweckt. Leider ist es der Thread innerhalb von p2, der zu dieser Zeit getötet wird.

Eine Beschreibung des Problems finden Sie unter https://lkml.org/lkml/2013/9/27/338.

Ich weiß nicht, ob es Kernel-Fixes/-Patches gibt. Weiß nicht, ob es überhaupt ein Fehler ist.

Trotzdem scheint es ein Workaround für das ganze Chaos.Verwenden robust mutexes mit PTHREAD_PRIO_INHERIT:

pthread_mutexattr_setprotocol(&ma, PTHREAD_PRIO_INHERIT); 

Innenkern (futex.c) statt handle_futex_death() einen anderen Mechanismus innerhalb exit_pi_state_list() die Spur tut Griff aus anderen Mutex Kellner. Es scheint das Problem zu lösen.