2010-12-31 2 views
6

ich ein Problem habe mit Valgrind mir zu sagen, ich etwas Speicher möglich verloren haben:pthread_create von pthread_detach gefolgt Ergebnisse noch in möglicherweise verloren Fehler in Valgrind

==23205== 544 bytes in 2 blocks are possibly lost in loss record 156 of 265 
==23205== at 0x6022879: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==23205== by 0x540E209: allocate_dtv (in /lib/ld-2.12.1.so) 
==23205== by 0x540E91D: _dl_allocate_tls (in /lib/ld-2.12.1.so) 
==23205== by 0x623068D: [email protected]@GLIBC_2.2.5 (in /lib/libpthread-2.12.1.so) 
==23205== by 0x758D66: MTPCreateThreadPool (MTP.c:290) 
==23205== by 0x405787: main (MServer.c:317) 

Der Code, der diese Fäden (MTPCreateThreadPool) schafft im Grunde bekommt ein Index in einen Block wartender pthread_t-Slots und erstellt daraus einen Thread. TI wird ein Zeiger auf eine Struktur, die einen Thread-Index und ein Pthread_t hat. (Vereinfacht/hygienisiert):

for (tindex = 0; tindex < NumThreads; tindex++) 
    { 
    int rc; 
    TI = &TP->ThreadInfo[tindex]; 
    TI->ThreadID = tindex; 

    rc = pthread_create(&TI->ThreadHandle,NULL,MTPHandleRequestsLoop,TI); 
    /* check for non-success that I've omitted */ 
    pthread_detach(&TI->ThreadHandle); 
    } 

Dann haben wir eine Funktion MTPDestroyThreadPool, die durch alle Fäden Schlaufen wir geschaffen und löscht sie (da die MTPHandleRequestsLoop nicht beendet).

for (tindex = 0; tindex < NumThreads; tindex++) 
    { 
    pthread_cancel(TP->ThreadInfo[tindex].ThreadHandle); 
    } 

Ich habe an anderer Stelle lesen (andere Fragen hier auf SO einschließlich), dass ein Gewinde Abnehmen explizit diese möglicherweise verloren Fehler verhindern würde, aber es ist eindeutig nicht. Irgendwelche Gedanken?

Antwort

4

Ein Grund könnte sein, dass pthread_cancel den Thread nicht wirklich abbricht - es ist nicht garantiert. Thread-Löschung ist asynchron; pthread_cancel kehrt sofort zurück, aber die Stornierung kann bis zum nächsten Löschpunkt verschoben werden. In diesem Fall sind die Threads möglicherweise noch vorhanden, wenn Valgrind Statistiken erfasst.

+0

Es war auch, was ich vorschlagen wollte, und möglicherweise den Rückgabewert von 'Pthread_cancel' testen –

+0

Also wenn einer der Threads (möglicherweise alle) auf einem Semaphor blockieren, wird der Abbruch sofort zurück, aber da sie ' Re blockiert es ist sehr wahrscheinlich, dass der Hauptserver-Prozess beendet wird, bevor sie tatsächlich abbrechen? – alesplin

+0

sem_wait ist ein Abbruchspunkt (http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?cancellation+5). Wenn also der Thread abgebrochen werden kann, sollte die Wartezeit sofort abgebrochen werden. Ein Thread, der sich in einer Endlosschleife dreht, ist ein besserer Kandidat. Sie sollten jedoch zuerst feststellen, ob das passiert, d. H. Ob die Threads abgebrochen werden oder nicht. –

8

Die Thread-Implementierung von glibc löscht absichtlich Speicher. Der Speicher, der einem Thread-Kontext zugewiesen wurde, wird zwischengespeichert, um ihn beim nächsten Erstellen eines Threads wiederzuverwenden. Ich habe ein Benchmarking im Vergleich zu einer Implementierung ohne Caching durchgeführt, und es scheint, dass das Caching die ansonsten optimale Zeit für pthread_create um 50% reduziert, aber für einen Nettoverlust drastisch pthread_join verlangsamt. Natürlich ist es immer noch ein (kleiner) Gewinn, wenn das, was Sie interessieren, die Latenz der Thread-Erstellung und nicht der Durchsatz ist.

Beachten Sie auch, dass es für einen abgetrennten Thread sehr schwierig ist, seinen Kontext aufzuheben, selbst wenn er es wollte. Mit einem verknüpfbaren Thread kann der Thread, der pthread_join aufruft, den Kontext freigeben, aber ein losgelöster Thread müsste während des Intervalls zwischen dem Freigeben seines Kontexts und dem Beenden des Kontextes ohne Stack arbeiten können. Dies kann nur erreicht werden, indem dieses kleine Stück Code in reinem asm geschrieben wird.

Fragen Sie sich, wie abgelöste Threads Kontexte ohne eine ähnliche Race-Bedingung in den Cache zurückgegeben werden? Linux verfügt über eine Funktion zum Ausschließen einer int an einer bestimmten Adresse (registriert von der Userspace-Thread-Bibliothek), wenn der Thread beendet wird. So kann der Thread dem Cache sicher seinen eigenen Kontext hinzufügen, da andere Threads bis zu ihrem Abschluss immer noch einen Wert ungleich Null (normalerweise seine Thread-ID) unter dieser Adresse sehen und interpretieren, dass der Kontext noch verwendet wird.

0

Das Erstellen eines Threads, der zusammengefügt werden kann, um ihn sofort zu entfernen, ergibt für mich nicht viel Sinn. Dies erzeugt nichts als Overhead für das System.

Ich würde die Threads von Anfang an losgelöst starten, haben Sie Ihre gemeinsame ThreadInfo Datenstruktur sowieso, um Ihre Threads zu steuern.

Auch ich würde eine Flagge oder etwas ähnliches in Ihrem pro Gewindeargument ThreadInfo haben, das dem Faden sagt, um in einer kontrollierten Weise herunterzufahren.

+0

Guter Punkt. Ich habe den fraglichen Code nicht geschrieben, ich suche nur nach der Ursache (und Heilung) der "möglicherweise verlorenen" Nachricht in Valgrind, aber ich kann das auch beheben, während ich dabei bin. – alesplin