LINUX.ORG.RU

[pthread][valgrind] join vs detach

 ,


0

1

Привет, ЛОР!
Написал некую программу на C, в ней достаточно активно используются треды. Жизнь треда начинается через pthread_create и заканчивается через pthread_cancel. Через pthread_attr функции pthread_create сообщается тип треда (JOINABLE или DETACHED). Вся динамически выделенная память освобождается в функции, назначенной треду через pthread_cleanup_push/pop.
После прогона программы под Valgrind выяснилось следующее:
* Если тред создан как JOINABLE (в этом случае после cancel вызывается join), то остаются possibly lost куски памяти, по одному на каждый созданный тред.
* Если тред создан как DETACHED (в этом случае после create дополнительно вызывается detach) вся память освобождается корректно.

Если верить man-у, то и join и detach освобождают все ресурсы, выделенные при создании треда.

В интернетах эту тему обсуждали в 2006-8 годах, некоторые предлагали вызывать detach после join и наоборот, но это попахивает бредом. Единого мнения я не нашел. Некоторые, кстати, считают, что на подобные утечки можно просто забить - дескать, все так и должно работать. Так ли это?

P.S.
Все это безобразие происходит под Ubuntu 9.10 x86


Ответ на: комментарий от anonymous

это так куллпроггеры называют потоки/нити

aho
()
Ответ на: комментарий от dmitry_vk

> * Если тред создан как JOINABLE (в этом случае после cancel вызывается join), ...

Да.

ifred
() автор топика

типичный log valgrind-а

==3379== HEAP SUMMARY:
==3379==     in use at exit: 4,636 bytes in 33 blocks
==3379==   total heap usage: 594 allocs, 561 frees, 1,555,208 bytes allocated
==3379== 
==3379== 28 bytes in 1 blocks are still reachable in loss record 1 of 2
==3379==    at 0x4024C1C: malloc (vg_replace_malloc.c:195)
==3379==    by 0x400C01E: _dl_map_object_deps (dl-deps.c:506)
==3379==    by 0x40117E0: dl_open_worker (dl-open.c:297)
==3379==    by 0x400D485: _dl_catch_error (dl-error.c:178)
==3379==    by 0x401119F: _dl_open (dl-open.c:586)
==3379==    by 0x42930C1: do_dlopen (dl-libc.c:86)
==3379==    by 0x400D485: _dl_catch_error (dl-error.c:178)
==3379==    by 0x42931C0: dlerror_run (dl-libc.c:47)
==3379==    by 0x42932DA: __libc_dlopen_mode (dl-libc.c:160)
==3379==    by 0x4185876: pthread_cancel_init (unwind-forcedunwind.c:53)
==3379==    by 0x41859EC: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==3379==    by 0x41834B7: __pthread_unwind (unwind.c:130)
==3379== 
==3379== 4,608 bytes in 32 blocks are possibly lost in loss record 2 of 2
==3379==    at 0x4023F5B: calloc (vg_replace_malloc.c:418)
==3379==    by 0x401094B: _dl_allocate_tls (dl-tls.c:300)
==3379==    by 0x417D102: pthread_create@@GLIBC_2.1 (allocatestack.c:561)
==3379==    by 0x804CB21: ews_socket_master_thread (ews_socket_threads.c:122)
==3379==    by 0x417C80D: start_thread (pthread_create.c:300)
==3379==    by 0x425CA0D: clone (clone.S:130)
==3379== 
==3379== LEAK SUMMARY:
==3379==    definitely lost: 0 bytes in 0 blocks
==3379==    indirectly lost: 0 bytes in 0 blocks
==3379==      possibly lost: 4,608 bytes in 32 blocks
==3379==    still reachable: 28 bytes in 1 blocks
==3379==         suppressed: 0 bytes in 0 blocks
==3379== 
==3379== For counts of detected and suppressed errors, rerun with: -v
==3379== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 27 from 6)

ifred
() автор топика
Ответ на: комментарий от ifred

Как проверить что она «реально есть»?

По идее, если это демон, то надо просто запустить его на пару дней, оно само вылезет.

nanoo_linux
()
Ответ на: комментарий от nanoo_linux

запустить его на пару дней, оно само вылезет.

Как я понимаю, так обычно и происходит :)

Однако, хочется верить, что если использовать сторонние библиотеки в соответствии с документацией, то они не будут давать утечек. Вот я и пытаюсь понять: это я не умею пользоваться pthread-ами или это в pthread-ах так хитро работают с памятью, что лучше о них не думать.

ifred
() автор топика
Ответ на: комментарий от ifred

Как я понимаю, так обычно и происходит :)

Ну ты сам подумай. Ежели у тебя possibly lost, то только эксперимент тебе покажет, так ли это или нет. Запусти на пару дней.

nanoo_linux
()
Ответ на: комментарий от nanoo_linux

Сколько оно у тебя работало для теста лог которого ты запостил? Там 4 кб. Если допустим 4 минуты, то это 1 кб/мин или 1.5 Мб в сутки где-то.

nanoo_linux
()
Ответ на: комментарий от ifred

Встречался с такой проблемой, без вызова pthread_detach после pthread_create, память после завершения потока не освобождалась.

midvik
()
Ответ на: комментарий от nanoo_linux

Короче с join что-то явно не так:
Скомпилировал с JOINABLE тредами. Воспользовался идеей про while true; do ./test-leak; done Программа выжрала все ресурсы за несколько секунд: htop показал, что VIRT равен объему моей оперативной памяти.
Потом попробовал тот же эксперимент с DETACHED тредами. Уже несколько минут все в полном порядке - объем памяти, занятый программой, имеет разумное значение и не меняется.
Оставлю на пару часов...

Но ведь в man-е (man pthread_detach) сказано:

The pthread_join() or pthread_detach() functions should eventually be called for every thread that is created so that storage associated with the thread may be reclaimed.

ifred
() автор топика

Еще один забавный момент: память утекает только в случае JOINABLE-тредов второго поколения (может и в случае третьего и т.д., но их у меня нет).

ifred
() автор топика
Ответ на: комментарий от ifred

> Скомпилировал с JOINABLE тредами. Воспользовался идеей про

while true; do ./test-leak; done

Программа выжрала все ресурсы за несколько секунд



хм. это вообще не имеет никакого отношения k libpthread.

какая «Программа» выжрала? test-leak же завершается.
покажи код.

idle ★★★★★
()
Ответ на: комментарий от idle

Я может не правильно понял, что такое test-leak.

Испытуемая программа является демоном. В моем понимании test-leak - утилита, каждый вызов которой заставляет демон породить и, затем, завершить один тред. Так вот, если в демоне используются JOINABLE-треды, то после запуска while true; do ./test-leak; done демон выжирает всю доступную память за считанные секунды. Если же используются DETACHED-треды, демон работает часами потребляя разумное количество ресурсов. В этом случае после принудительного завершения valgrind показывает, что утечек нет (точнее есть долбаные still reachable 28 байт, но valgrind не считает это ошибкой).

ifred
() автор топика
Ответ на: комментарий от ifred

> test-leak - утилита, каждый вызов которой заставляет демон

породить и, затем, завершить один тред.


а.

ну, тогда попробуй написать минимальний test-case который
делает create/join в цикле.

может, это у тебя где-то течет... ну, и в glibc bug может быть.

idle ★★★★★
()
Ответ на: комментарий от idle

Мысль про минимальный test-case хороша. Попробую.
Забавно будет, если придется bug-report писать...

ifred
() автор топика

Как несложно догадаться, никаких утечек в glibc не было, проблема была в моем коде. Если после завершения JOINABLE-треда вызывать join, а в случае DETACHED-треда выходить из основной программы заведомо после завершения всех cleanup-процедур, то память не течет вообще.
Главное - внимательно читать man :)

ifred
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.