I connect a TCP stream socket to a given hostname and port. To get the host's IP address I use getaddrinfo() as follows
struct addrinfo hints;
struct addrinfo *result, *server;
memset(&hints, 0, sizeof(struct addrinfo)); // fill server with 0
hints.ai_family = AF_INET; // set address family
hints.ai_protocol=0; // set protocol type to auto
hints.ai_socktype = SOCK_STREAM; // set type of socket to stream socket
if(getaddrinfo(HOSTNAME,PORT,&hints,&result)){
perror("Could not resolve IP from given hostname and port (getaddrinfo)");
freeaddrinfo(result);
return -1;
}
if(print) printf("Connecting socket ... \n");
int connectFlag=1;
for(server=result;server!=NULL;server=server->ai_next){
if(connect(sockfd,server->ai_addr,server->ai_addrlen)==-1){
continue;
}
if(print){
printf("Socket successfully connected\n");
}
break;
}
if(!connectFlag){
printf("Could not connect to server %s.\n",HOSTNAME);
close(sockfd);
freeaddrinfo(result);
return -1;
}
freeaddrinfo(result);
All this happens inside a function that reveivecs a parameter
int sockfd = socket(AF_INET,SOCK_STREAM,0);
from another function. Everything works out fine, the socket connects and I receive and send messages as expected.
The problem is, when I check my created binary with
valgrind --leak-check=full --trace-children=yes ./binary
I get 3313 bytes are still reachable. When resolve the host manually and connect directly using the IP I get no leaks. I replaced getaddrinfo() with the older gethostbyname() and got the exact same amount of still reachable bytes.
How can I get rid of these leaks?
Valgrind output
% valgrind --leak-check=full --show-leak-kinds=all ./play
==16353== Memcheck, a memory error detector
==16353== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==16353== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==16353== Command: ./a.out
==16353==
==16353==
==16353== HEAP SUMMARY:
==16353== in use at exit: 3,313 bytes in 10 blocks
==16353== total heap usage: 96 allocs, 86 frees, 54,234 bytes allocated
==16353==
==16353== 64 bytes in 2 blocks are still reachable in loss record 1 of 5
==16353== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353== by 0x4016790: _dl_close_worker.part.0 (dl-close.c:393)
==16353== by 0x4017029: _dl_close_worker (dl-close.c:125)
==16353== by 0x4017029: _dl_close (dl-close.c:822)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x4F7CBAE: dlerror_run (dl-libc.c:46)
==16353== by 0x4F7CBAE: __libc_dlclose (dl-libc.c:222)
==16353== by 0x4FADB66: free_mem (in /lib/x86_64-linux-gnu/libc-2.23.so)
==16353== by 0x4FAD78F: __libc_freeres (in /lib/x86_64-linux-gnu/libc-2.23.so)
==16353== by 0x4A2868C: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so)
==16353== by 0x4E73FAA: __run_exit_handlers (exit.c:97)
==16353== by 0x4E74044: exit (exit.c:104)
==16353== by 0x404162: cleanUp (in /home/a)
==16353== by 0x401CB7: dispatch (in /home/a)
==16353==
==16353== 71 bytes in 2 blocks are still reachable in loss record 2 of 5
==16353== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353== by 0x401CD89: strdup (strdup.c:42)
==16353== by 0x401860E: _dl_load_cache_lookup (dl-cache.c:311)
==16353== by 0x4008F98: _dl_map_object (dl-load.c:2342)
==16353== by 0x400D9D1: openaux (dl-deps.c:63)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x400E011: _dl_map_object_deps (dl-deps.c:254)
==16353== by 0x4015411: dl_open_worker (dl-open.c:280)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x4014BD8: _dl_open (dl-open.c:660)
==16353== by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==
==16353== 71 bytes in 2 blocks are still reachable in loss record 3 of 5
==16353== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353== by 0x400BD23: _dl_new_object (dl-object.c:165)
==16353== by 0x400633C: _dl_map_object_from_fd (dl-load.c:1006)
==16353== by 0x4008A56: _dl_map_object (dl-load.c:2476)
==16353== by 0x400D9D1: openaux (dl-deps.c:63)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x400E011: _dl_map_object_deps (dl-deps.c:254)
==16353== by 0x4015411: dl_open_worker (dl-open.c:280)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x4014BD8: _dl_open (dl-open.c:660)
==16353== by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==
==16353== 744 bytes in 2 blocks are still reachable in loss record 4 of 5
==16353== at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353== by 0x4011EED: _dl_check_map_versions (dl-version.c:293)
==16353== by 0x4015948: dl_open_worker (dl-open.c:286)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x4014BD8: _dl_open (dl-open.c:660)
==16353== by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x4F7CA73: dlerror_run (dl-libc.c:46)
==16353== by 0x4F7CA73: __libc_dlopen_mode (dl-libc.c:163)
==16353== by 0x4F623BD: nss_load_library (nsswitch.c:358)
==16353== by 0x4F630F3: __nss_lookup_function (nsswitch.c:466)
==16353== by 0x4F630F3: __nss_next2 (nsswitch.c:242)
==16353== by 0x4F535FA: gethostbyname2_r##GLIBC_2.2.5 (getXXbyYY_r.c:281)
==16353== by 0x4F259DE: gaih_inet (getaddrinfo.c:622)
==16353==
==16353== 2,363 bytes in 2 blocks are still reachable in loss record 5 of 5
==16353== at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353== by 0x400BA25: _dl_new_object (dl-object.c:75)
==16353== by 0x400633C: _dl_map_object_from_fd (dl-load.c:1006)
==16353== by 0x4008A56: _dl_map_object (dl-load.c:2476)
==16353== by 0x400D9D1: openaux (dl-deps.c:63)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x400E011: _dl_map_object_deps (dl-deps.c:254)
==16353== by 0x4015411: dl_open_worker (dl-open.c:280)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== by 0x4014BD8: _dl_open (dl-open.c:660)
==16353== by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353== by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==
==16353== LEAK SUMMARY:
==16353== definitely lost: 0 bytes in 0 blocks
==16353== indirectly lost: 0 bytes in 0 blocks
==16353== possibly lost: 0 bytes in 0 blocks
==16353== still reachable: 3,313 bytes in 10 blocks
==16353== suppressed: 0 bytes in 0 blocks
==16353==
==16353== For counts of detected and suppressed errors, rerun with: -v
==16353== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
If I add the valgrind option --trace-children=yes valgrind checks a child process. I did not output this because the leak is happening in this parent process.
EDIT
It turned out we were not able to reproduce the valgrind output on other hardware. On this one server getaddrinfo() produced this valgrind output as well as gethostbyname(). We did not further observed where this behaviour might come from.
"Still reachable" means that the program has allocated the memory AND it still has a reference to it at the time of termination. It probably means that getaddrinfo() and gethostbyname() on the first call allocates memory which is reused.
This SO-answer might also be applicable: https://stackoverflow.com/a/13230399/2696475
Related
I'm using pthread in my program. It runs fine but valgrind detects still reachables. Always the same bytes: 1654, 4 blocks. Always the same functions visible in valgrind.
Valgrind log:
==29908== Memcheck, a memory error detector
==29908== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29908== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==29908== Command: ./a.out
==29908== Parent PID: 23902
==29908==
==29908==
==29908== HEAP SUMMARY:
==29908== in use at exit: 1,654 bytes in 4 blocks
==29908== total heap usage: 8 allocs, 4 frees, 2,526 bytes allocated
==29908==
==29908== 36 bytes in 1 blocks are still reachable in loss record 1 of 4
==29908== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==29908== by 0x401F5CE: strdup (strdup.c:42)
==29908== by 0x4019A81: _dl_load_cache_lookup (dl-cache.c:338)
==29908== by 0x400A989: _dl_map_object (dl-load.c:2102)
==29908== by 0x4015D36: dl_open_worker (dl-open.c:513)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x40155F9: _dl_open (dl-open.c:837)
==29908== by 0x49DE860: do_dlopen (dl-libc.c:96)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x49DF982: _dl_catch_error (dl-error-skeleton.c:227)
==29908== by 0x49DE994: dlerror_run (dl-libc.c:46)
==29908== by 0x49DE994: __libc_dlopen_mode (dl-libc.c:195)
==29908== by 0x486E99A: pthread_cancel_init (unwind-forcedunwind.c:53)
==29908==
==29908== 36 bytes in 1 blocks are still reachable in loss record 2 of 4
==29908== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==29908== by 0x400D5A7: _dl_new_object (dl-object.c:196)
==29908== by 0x4006E96: _dl_map_object_from_fd (dl-load.c:997)
==29908== by 0x400A61A: _dl_map_object (dl-load.c:2236)
==29908== by 0x4015D36: dl_open_worker (dl-open.c:513)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x40155F9: _dl_open (dl-open.c:837)
==29908== by 0x49DE860: do_dlopen (dl-libc.c:96)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x49DF982: _dl_catch_error (dl-error-skeleton.c:227)
==29908== by 0x49DE994: dlerror_run (dl-libc.c:46)
==29908== by 0x49DE994: __libc_dlopen_mode (dl-libc.c:195)
==29908== by 0x486E99A: pthread_cancel_init (unwind-forcedunwind.c:53)
==29908==
==29908== 384 bytes in 1 blocks are still reachable in loss record 3 of 4
==29908== at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==29908== by 0x401330A: _dl_check_map_versions (dl-version.c:274)
==29908== by 0x40160EC: dl_open_worker (dl-open.c:577)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x40155F9: _dl_open (dl-open.c:837)
==29908== by 0x49DE860: do_dlopen (dl-libc.c:96)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x49DF982: _dl_catch_error (dl-error-skeleton.c:227)
==29908== by 0x49DE994: dlerror_run (dl-libc.c:46)
==29908== by 0x49DE994: __libc_dlopen_mode (dl-libc.c:195)
==29908== by 0x486E99A: pthread_cancel_init (unwind-forcedunwind.c:53)
==29908== by 0x486EBB3: _Unwind_ForcedUnwind (unwind-forcedunwind.c:127)
==29908== by 0x486CF05: __pthread_unwind (unwind.c:121)
==29908==
==29908== 1,198 bytes in 1 blocks are still reachable in loss record 4 of 4
==29908== at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==29908== by 0x400D273: _dl_new_object (dl-object.c:89)
==29908== by 0x4006E96: _dl_map_object_from_fd (dl-load.c:997)
==29908== by 0x400A61A: _dl_map_object (dl-load.c:2236)
==29908== by 0x4015D36: dl_open_worker (dl-open.c:513)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x40155F9: _dl_open (dl-open.c:837)
==29908== by 0x49DE860: do_dlopen (dl-libc.c:96)
==29908== by 0x49DF8B7: _dl_catch_exception (dl-error-skeleton.c:208)
==29908== by 0x49DF982: _dl_catch_error (dl-error-skeleton.c:227)
==29908== by 0x49DE994: dlerror_run (dl-libc.c:46)
==29908== by 0x49DE994: __libc_dlopen_mode (dl-libc.c:195)
==29908== by 0x486E99A: pthread_cancel_init (unwind-forcedunwind.c:53)
==29908==
==29908== LEAK SUMMARY:
==29908== definitely lost: 0 bytes in 0 blocks
==29908== indirectly lost: 0 bytes in 0 blocks
==29908== possibly lost: 0 bytes in 0 blocks
==29908== still reachable: 1,654 bytes in 4 blocks
==29908== suppressed: 0 bytes in 0 blocks
==29908==
==29908== For lists of detected and suppressed errors, rerun with: -s
==29908== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
If I limit the program to 1 thread, I never get any still reachables. If I limit it to something low like 2, I sometimes do, sometimes don't. Anything high (48) means I almost always get still reachables.
I managed to reproduce the problem without much code, as follows. What am I doing wrong? If I am doing nothing wrong and it's just pthread things, why is it making still reachables? And why is it semi-random depending on amount of threads?
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUMTHREADS 2 //can be anything higher than 1 to cause still reachables
void *thread(void *arg)
{
(void)arg;
pthread_exit(EXIT_SUCCESS);
}
int main(void)
{
int i;
pthread_t threads[NUMTHREADS];
i = -1;
while (++i < NUMTHREADS)
pthread_create(threads + i, NULL, &thread, NULL);
i = -1;
while (++i < NUMTHREADS)
pthread_join(threads[i], NULL);
return (0);
}
In your thread function, you are doing:
pthread_exit(EXIT_SUCCESS);
Replace with:
return (void *) 0;
Perfect, thank you. Any downsides to exiting threads this way? Do you also have any idea what in pthread is causing these still reachables or why this solution works? –
Modin
The [linux, at least] implementation of pthread_create does not put the argument you give it for the thread function (e.g. in your case thread) as the thread start address given to the clone syscall. It gives the pointer to an internal/hidden "helper" start function that does:
Some initialization of thread local storage, etc (which may need to call malloc).
Invokes the "real" function
TLS cleanup/destructors
If we call pthread_exit, we are bypassing step (3).
To see the details, we'd have to get the glibc source and look in nptl/pthread_create.c to the 10 or so steps that get bypassed by not returning.
Personally, I've always "unwound" my call stack to use the return (void *) and only called pthread_exit as an "abort"
Edit:
If we look at the source for pthread_exit, it calls __do_cancel [an internal function]. So, it's like doing:
pthread_cancel(pthread_self());
Given the [modified] program:
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUMTHREADS 8 // can be anything higher than 1 to cause still reachables
void *
thread(void *arg)
{
#if DOEXIT
(void) arg;
pthread_exit(EXIT_SUCCESS);
#else
return (void *) 0;
#endif
}
int
main(void)
{
int i;
pthread_t threads[NUMTHREADS];
i = -1;
while (++i < NUMTHREADS)
pthread_create(threads + i, NULL, &thread, NULL);
i = -1;
while (++i < NUMTHREADS)
pthread_join(threads[i], NULL);
return (0);
}
Note in the test outputs below, the valgrind command is:
valgrind --leak-check=full --show-leak-kinds=all -s ./fix0
Here is the output compiled with -DDOEXIT=1 (that does pthread_exit):
==1659955== Memcheck, a memory error detector
==1659955== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1659955== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==1659955== Command: ./fix0
==1659955==
==1659955==
==1659955== HEAP SUMMARY:
==1659955== in use at exit: 1,616 bytes in 4 blocks
==1659955== total heap usage: 13 allocs, 9 frees, 3,848 bytes allocated
==1659955==
==1659955== 21 bytes in 1 blocks are still reachable in loss record 1 of 4
==1659955== at 0x483780B: malloc (vg_replace_malloc.c:309)
==1659955== by 0x401C13D: strdup (strdup.c:42)
==1659955== by 0x4016D0A: _dl_load_cache_lookup (dl-cache.c:306)
==1659955== by 0x4009592: _dl_map_object (dl-load.c:2107)
==1659955== by 0x4013A7D: dl_open_worker (dl-open.c:217)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x401363D: _dl_open (dl-open.c:588)
==1659955== by 0x49F5250: do_dlopen (dl-libc.c:96)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x49F5E02: _dl_catch_error (dl-error-skeleton.c:215)
==1659955== by 0x49F5356: dlerror_run (dl-libc.c:46)
==1659955== by 0x49F53E9: __libc_dlopen_mode (dl-libc.c:195)
==1659955==
==1659955== 21 bytes in 1 blocks are still reachable in loss record 2 of 4
==1659955== at 0x483780B: malloc (vg_replace_malloc.c:309)
==1659955== by 0x400BCEF: _dl_new_object (dl-object.c:163)
==1659955== by 0x400649F: _dl_map_object_from_fd (dl-load.c:1002)
==1659955== by 0x4009319: _dl_map_object (dl-load.c:2241)
==1659955== by 0x4013A7D: dl_open_worker (dl-open.c:217)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x401363D: _dl_open (dl-open.c:588)
==1659955== by 0x49F5250: do_dlopen (dl-libc.c:96)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x49F5E02: _dl_catch_error (dl-error-skeleton.c:215)
==1659955== by 0x49F5356: dlerror_run (dl-libc.c:46)
==1659955== by 0x49F53E9: __libc_dlopen_mode (dl-libc.c:195)
==1659955==
==1659955== 384 bytes in 1 blocks are still reachable in loss record 3 of 4
==1659955== at 0x4839B1A: calloc (vg_replace_malloc.c:762)
==1659955== by 0x401142F: _dl_check_map_versions (dl-version.c:274)
==1659955== by 0x4013B25: dl_open_worker (dl-open.c:266)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x401363D: _dl_open (dl-open.c:588)
==1659955== by 0x49F5250: do_dlopen (dl-libc.c:96)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x49F5E02: _dl_catch_error (dl-error-skeleton.c:215)
==1659955== by 0x49F5356: dlerror_run (dl-libc.c:46)
==1659955== by 0x49F53E9: __libc_dlopen_mode (dl-libc.c:195)
==1659955== by 0x48AE45A: pthread_cancel_init (unwind-forcedunwind.c:53)
==1659955== by 0x48AE673: _Unwind_ForcedUnwind (unwind-forcedunwind.c:127)
==1659955==
==1659955== 1,190 bytes in 1 blocks are still reachable in loss record 4 of 4
==1659955== at 0x4839B1A: calloc (vg_replace_malloc.c:762)
==1659955== by 0x400BA11: _dl_new_object (dl-object.c:73)
==1659955== by 0x400649F: _dl_map_object_from_fd (dl-load.c:1002)
==1659955== by 0x4009319: _dl_map_object (dl-load.c:2241)
==1659955== by 0x4013A7D: dl_open_worker (dl-open.c:217)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x401363D: _dl_open (dl-open.c:588)
==1659955== by 0x49F5250: do_dlopen (dl-libc.c:96)
==1659955== by 0x49F5D66: _dl_catch_exception (dl-error-skeleton.c:196)
==1659955== by 0x49F5E02: _dl_catch_error (dl-error-skeleton.c:215)
==1659955== by 0x49F5356: dlerror_run (dl-libc.c:46)
==1659955== by 0x49F53E9: __libc_dlopen_mode (dl-libc.c:195)
==1659955==
==1659955== LEAK SUMMARY:
==1659955== definitely lost: 0 bytes in 0 blocks
==1659955== indirectly lost: 0 bytes in 0 blocks
==1659955== possibly lost: 0 bytes in 0 blocks
==1659955== still reachable: 1,616 bytes in 4 blocks
==1659955== suppressed: 0 bytes in 0 blocks
==1659955==
==1659955== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Here is the output when compiled with -DDOEXIT=0 (does return (void *) 0;):
==1660133== Memcheck, a memory error detector
==1660133== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1660133== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==1660133== Command: ./fix0
==1660133==
==1660133==
==1660133== HEAP SUMMARY:
==1660133== in use at exit: 0 bytes in 0 blocks
==1660133== total heap usage: 8 allocs, 8 frees, 2,176 bytes allocated
==1660133==
==1660133== All heap blocks were freed -- no leaks are possible
==1660133==
==1660133== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
While debugging a random segmentation fault, I ran valgrind, running the same program multiple times I get different results. Here is a simplified version of the program (I did not get a a random segmentation fault on this simplified version):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
pthread_t* tid;
pthread_t prodId;
int numberThreads = 0;
void *worker(void *arg);
void *producer(void *arg);
int main(int argc, char** argv) {
numberThreads = atoi((char *) argv[1]);
tid = (pthread_t*) malloc(sizeof (pthread_t) * numberThreads);
pthread_create(&prodId, NULL, producer, &prodId);
pthread_join(prodId, NULL);
free(tid);
return (EXIT_SUCCESS);
}
void *producer(void *arg) {
for (int x = 0; x < numberThreads; x++)
pthread_create(&(tid[x]), NULL, worker, &(tid[x]));
for (int x = 0; x < numberThreads; x++)
pthread_join(tid[x], NULL);
pthread_exit(NULL);
}
void *worker(void *arg) {
pthread_exit(NULL);
}
Here is the output of running the program a few times consecutively:
$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all ./memleek 1
==3044== Memcheck, a memory error detector
==3044== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3044== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3044== Command: ./memleek 1
==3044==
==3044==
==3044== HEAP SUMMARY:
==3044== in use at exit: 0 bytes in 0 blocks
==3044== total heap usage: 8 allocs, 8 frees, 2,222 bytes allocated
==3044==
==3044== All heap blocks were freed -- no leaks are possible
==3044==
==3044== For counts of detected and suppressed errors, rerun with: -v
==3044== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$
$
$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all ./memleek 2
==3047== Memcheck, a memory error detector
==3047== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3047== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3047== Command: ./memleek 2
==3047==
==3047==
==3047== HEAP SUMMARY:
==3047== in use at exit: 0 bytes in 0 blocks
==3047== total heap usage: 9 allocs, 9 frees, 2,502 bytes allocated
==3047==
==3047== All heap blocks were freed -- no leaks are possible
==3047==
==3047== For counts of detected and suppressed errors, rerun with: -v
==3047== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$
$
$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all ./memleek 2
==3051== Memcheck, a memory error detector
==3051== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3051== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3051== Command: ./memleek 2
==3051==
==3051==
==3051== HEAP SUMMARY:
==3051== in use at exit: 0 bytes in 0 blocks
==3051== total heap usage: 9 allocs, 9 frees, 2,502 bytes allocated
==3051==
==3051== All heap blocks were freed -- no leaks are possible
==3051==
==3051== For counts of detected and suppressed errors, rerun with: -v
==3051== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$
$
$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all ./memleek 2
==3055== Memcheck, a memory error detector
==3055== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3055== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3055== Command: ./memleek 2
==3055==
==3055==
==3055== HEAP SUMMARY:
==3055== in use at exit: 1,614 bytes in 4 blocks
==3055== total heap usage: 9 allocs, 5 frees, 2,502 bytes allocated
==3055==
==3055== 36 bytes in 1 blocks are still reachable in loss record 1 of 4
==3055== at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
==3055== by 0x401AE89: strdup (strdup.c:42)
==3055== by 0x4016676: _dl_load_cache_lookup (dl-cache.c:311)
==3055== by 0x4008A87: _dl_map_object (dl-load.c:2336)
==3055== by 0x4013B13: dl_open_worker (dl-open.c:237)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x4013608: _dl_open (dl-open.c:660)
==3055== by 0x517431C: do_dlopen (dl-libc.c:87)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055== by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055== by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055==
==3055== 36 bytes in 1 blocks are still reachable in loss record 2 of 4
==3055== at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
==3055== by 0x400B4D3: _dl_new_object (dl-object.c:165)
==3055== by 0x400587C: _dl_map_object_from_fd (dl-load.c:1000)
==3055== by 0x400874B: _dl_map_object (dl-load.c:2470)
==3055== by 0x4013B13: dl_open_worker (dl-open.c:237)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x4013608: _dl_open (dl-open.c:660)
==3055== by 0x517431C: do_dlopen (dl-libc.c:87)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055== by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055== by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055==
==3055== 360 bytes in 1 blocks are still reachable in loss record 3 of 4
==3055== at 0x4C2DBC5: calloc (vg_replace_malloc.c:711)
==3055== by 0x4010FBD: _dl_check_map_versions (dl-version.c:293)
==3055== by 0x4014076: dl_open_worker (dl-open.c:286)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x4013608: _dl_open (dl-open.c:660)
==3055== by 0x517431C: do_dlopen (dl-libc.c:87)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055== by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055== by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055== by 0x4E49853: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==3055== by 0x4E47D7F: __pthread_unwind (unwind.c:121)
==3055==
==3055== 1,182 bytes in 1 blocks are still reachable in loss record 4 of 4
==3055== at 0x4C2DBC5: calloc (vg_replace_malloc.c:711)
==3055== by 0x400B215: _dl_new_object (dl-object.c:75)
==3055== by 0x400587C: _dl_map_object_from_fd (dl-load.c:1000)
==3055== by 0x400874B: _dl_map_object (dl-load.c:2470)
==3055== by 0x4013B13: dl_open_worker (dl-open.c:237)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x4013608: _dl_open (dl-open.c:660)
==3055== by 0x517431C: do_dlopen (dl-libc.c:87)
==3055== by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055== by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055== by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055== by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055==
==3055== LEAK SUMMARY:
==3055== definitely lost: 0 bytes in 0 blocks
==3055== indirectly lost: 0 bytes in 0 blocks
==3055== possibly lost: 0 bytes in 0 blocks
==3055== still reachable: 1,614 bytes in 4 blocks
==3055== suppressed: 0 bytes in 0 blocks
==3055==
==3055== For counts of detected and suppressed errors, rerun with: -v
==3055== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$
I noticed that if I run 4 workers, by passing 4 as the first argument, it constantly says
still reachable: 1,614 bytes in 4 blocks
Considering that according to this, the "still reachable" category within Valgrind's leak report refers to allocations that fit only the first definition of "memory leak". These blocks were not freed, but they could have been freed (if the programmer had wanted to) because the program still was keeping track of pointers to those memory blocks.
Well, I want to free these blocks. Can anyone show me how? Thank you!
I tried running your program and I saw the same behavior. I also found that removing one row, the pthread_exit in the workers, removed the valgrind warning about still reachable memory.
I started to suspect glibc, and this was confirmed by a similar SO question/answer:
glibc oddity with Valgrind
I started to learn about POSIX threads and about freeing memory from function called through pthread_create which has inside a malloc call.
So I came to the following program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *foo( void *vargp );
int main( void )
{
pthread_t thread_id[5];
const char *msg = "Hello\n";
char **ptr = NULL;
printf("Before Thread\n");
for( int i = 0 ; i < 5 ; i++ )
{
pthread_create(&thread_id[i], NULL, foo, (void*)msg);
}
for( int i = 0 ; i < 5 ; i++ )
{
pthread_join(thread_id[i], ( void* )&ptr );
free( ptr );
}
printf("After Thread\n");
}
void *foo( void *vargp )
{
char *ptr = malloc( 256 * sizeof( * ptr ) );
strcpy( ptr, (char*)vargp );
printf("The message is: %s \n", ptr );
pthread_exit( ptr );
}
after I run valgrind:
valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all ./Demo
And I get the following Output, which seems to be OK with the memory and there is no leak reported by valgrind:
==5255== Memcheck, a memory error detector
==5255== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5255== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5255== Command: ./Demo
==5255==
Before Thread
The message is: Hello
The message is: Hello
The message is: Hello
The message is: Hello
The message is: Hello
After Thread
==5255==
==5255== HEAP SUMMARY:
==5255== in use at exit: 0 bytes in 0 blocks
==5255== total heap usage: 16 allocs, 16 frees, 5,358 bytes allocated
==5255==
==5255== All heap blocks were freed -- no leaks are possible
==5255==
==5255== For counts of detected and suppressed errors, rerun with: -v
==5255== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Now here is something which I do not understand, if I remove de \n from here:
const char *msg = "Hello\n";
And I compile and run valgrind again, I get:
==5279== Memcheck, a memory error detector
==5279== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5279== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5279== Command: ./Demo
==5279==
Before Thread
The message is: Hello
The message is: Hello
The message is: Hello
The message is: Hello
The message is: Hello
After Thread
==5279==
==5279== HEAP SUMMARY:
==5279== in use at exit: 1,638 bytes in 4 blocks
==5279== total heap usage: 16 allocs, 12 frees, 5,358 bytes allocated
==5279==
==5279== 36 bytes in 1 blocks are still reachable in loss record 1 of 4
==5279== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5279== by 0x401D329: strdup (strdup.c:42)
==5279== by 0x4018656: _dl_load_cache_lookup (dl-cache.c:315)
==5279== by 0x4009382: _dl_map_object (dl-load.c:2255)
==5279== by 0x4014EE3: dl_open_worker (dl-open.c:235)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x40147C9: _dl_open (dl-open.c:605)
==5279== by 0x53C83AC: do_dlopen (dl-libc.c:96)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x53C936E: _dl_catch_error (dl-error-skeleton.c:215)
==5279== by 0x53C84D8: dlerror_run (dl-libc.c:46)
==5279== by 0x53C84D8: __libc_dlopen_mode (dl-libc.c:195)
==5279== by 0x5055DEA: pthread_cancel_init (unwind-forcedunwind.c:52)
==5279==
==5279== 36 bytes in 1 blocks are still reachable in loss record 2 of 4
==5279== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5279== by 0x400C3E7: _dl_new_object (dl-object.c:163)
==5279== by 0x40069A4: _dl_map_object_from_fd (dl-load.c:943)
==5279== by 0x4008FFB: _dl_map_object (dl-load.c:2389)
==5279== by 0x4014EE3: dl_open_worker (dl-open.c:235)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x40147C9: _dl_open (dl-open.c:605)
==5279== by 0x53C83AC: do_dlopen (dl-libc.c:96)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x53C936E: _dl_catch_error (dl-error-skeleton.c:215)
==5279== by 0x53C84D8: dlerror_run (dl-libc.c:46)
==5279== by 0x53C84D8: __libc_dlopen_mode (dl-libc.c:195)
==5279== by 0x5055DEA: pthread_cancel_init (unwind-forcedunwind.c:52)
==5279==
==5279== 384 bytes in 1 blocks are still reachable in loss record 3 of 4
==5279== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5279== by 0x4011E85: _dl_check_map_versions (dl-version.c:274)
==5279== by 0x401524B: dl_open_worker (dl-open.c:284)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x40147C9: _dl_open (dl-open.c:605)
==5279== by 0x53C83AC: do_dlopen (dl-libc.c:96)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x53C936E: _dl_catch_error (dl-error-skeleton.c:215)
==5279== by 0x53C84D8: dlerror_run (dl-libc.c:46)
==5279== by 0x53C84D8: __libc_dlopen_mode (dl-libc.c:195)
==5279== by 0x5055DEA: pthread_cancel_init (unwind-forcedunwind.c:52)
==5279== by 0x5055FD3: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==5279== by 0x5053F0F: __pthread_unwind (unwind.c:121)
==5279==
==5279== 1,182 bytes in 1 blocks are still reachable in loss record 4 of 4
==5279== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5279== by 0x400C100: _dl_new_object (dl-object.c:73)
==5279== by 0x40069A4: _dl_map_object_from_fd (dl-load.c:943)
==5279== by 0x4008FFB: _dl_map_object (dl-load.c:2389)
==5279== by 0x4014EE3: dl_open_worker (dl-open.c:235)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x40147C9: _dl_open (dl-open.c:605)
==5279== by 0x53C83AC: do_dlopen (dl-libc.c:96)
==5279== by 0x53C92DE: _dl_catch_exception (dl-error-skeleton.c:196)
==5279== by 0x53C936E: _dl_catch_error (dl-error-skeleton.c:215)
==5279== by 0x53C84D8: dlerror_run (dl-libc.c:46)
==5279== by 0x53C84D8: __libc_dlopen_mode (dl-libc.c:195)
==5279== by 0x5055DEA: pthread_cancel_init (unwind-forcedunwind.c:52)
==5279==
==5279== LEAK SUMMARY:
==5279== definitely lost: 0 bytes in 0 blocks
==5279== indirectly lost: 0 bytes in 0 blocks
==5279== possibly lost: 0 bytes in 0 blocks
==5279== still reachable: 1,638 bytes in 4 blocks
==5279== suppressed: 0 bytes in 0 blocks
==5279==
==5279== For counts of detected and suppressed errors, rerun with: -v
==5279== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
So I have 3 Question:
1) - did I free() the malloc correctly
2) - Why I get that output if I remove \n
3) - Do I have (must) to free() that memory inside foo?
To me looks like my Program (somehow) is UB.
1) yes you have correctly free'd the memory
2) the OS internally sometime does some bizarre things that valgrind does not understand. If you do not want to see these messages then you should suppressing rules (http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress)
3) no, as long as you free the memory before the end of the process, it is not a memory leak
so I'm updating the code again as recommended by useless so that you can see something that you can compile and run. I cleaned the code a bit from other things, but the problem remains.Executing it with valgrind when I give the SIGINT signal the program ends by printing the terminated threads correctly, but sometimes it ends without memory leaks, others reporting some memory leaks that I leave behind. At the moment I am doing tests without connected clients.
I am trying to create a multithreaded server that uses the select() function and a Thread Pool for a university project. Executing my code with Valgrind I see that after sending a SIGINT signal, sometimes it terminates without errors, while other times it reports 4 "still reachable" errors.
I launch the program with valgrind specifying the following flags:
valgrind --leak-check=full --show-leak-kinds=all
This is the new code
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <sys/select.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#define UNIXPATH "./tmp/socket"
#define THREADSINPOOL 8
#define MAXCONNECTION 32
#define SYSCALL(r,c,msg) \
if((r=c)==-1) {perror(msg); exit(errno); }
typedef struct _elem{
long connfd;
struct _elem *next;
}elem;
static volatile sig_atomic_t terminate=0;
int try=0;
int nelem=0;
elem *head=NULL;
elem *corr=NULL;
elem *tmp=NULL;
pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t empty=PTHREAD_COND_INITIALIZER;
pthread_cond_t full=PTHREAD_COND_INITIALIZER;
int update(fd_set set, int fdmax){
for(int i=(fdmax-1);i>=0; i--)
if(FD_ISSET(i,&set)) return i;
return -1;
}
void *threadF(void *arg){
pthread_t self;
long connfd=0;
while(1){
pthread_mutex_lock(&mtx);
if(try==1){
pthread_mutex_unlock(&mtx);
break;
}
while(nelem==0 && try==0)
pthread_cond_wait(&empty,&mtx);
if(try==1){
pthread_mutex_unlock(&mtx);
break;
}
nelem--;
tmp=head;
connfd=tmp->connfd;
tmp=tmp->next;
free(head);
head=tmp;
pthread_cond_signal(&full);
pthread_mutex_unlock(&mtx);
//read & write on tmp->connfd
self = pthread_self();
printf("tmp->connfd: %lu on thread: %lu\n", connfd,self);
close(connfd);
}
pthread_exit(0);
}
void insert(long connfd){
pthread_mutex_lock(&mtx);
while(nelem==MAXCONNECTION && try==0)
pthread_cond_wait(&full,&mtx);
if(try==1){
pthread_mutex_unlock(&mtx);
}else{
nelem++;
elem *new=malloc(sizeof(elem));
new->connfd=connfd;
new->next=NULL;
if(head==NULL){
head=new;
corr=head;
}else{
corr->next=new;
corr=corr->next;
}
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mtx);
}
}
pthread_t *createarray(pthread_t *array){
int i,err;
pthread_t id;
for(i=0;i<THREADSINPOOL;i++){
if((err=pthread_create(&id,NULL,&threadF,NULL))!=0){
fprintf(stderr,"thread\n");
exit(errno);
}
array[i]=id;
}
return array;
}
void destroyArray(pthread_t *array){
void* value=NULL;
int i,tmp;
for (i = 0; i < THREADSINPOOL; i++){
if ((tmp=pthread_join(array[i],&value)) != 0)
printf("error join: %d\n", tmp);
printf("thread: %lu terminated\n",array[i]);
}
free(array);
}
void cleanup(){
unlink(UNIXPATH);
}
void sigint_handler(int signmum){
terminate=1;
}
int main(int argc, char *argv[]) {
cleanup();
atexit(cleanup);
int notused;
sigset_t setmask;
SYSCALL(notused,sigemptyset(&setmask),"SIGEMPTYSET");
SYSCALL(notused,sigaddset(&setmask,SIGINT),"SIGADDSET");
SYSCALL(notused,pthread_sigmask(SIG_SETMASK,&setmask,NULL),"pthread_sigmask");
//create threadspool
pthread_t *array=malloc(THREADSINPOOL*sizeof(pthread_t));
array=createarray(array);
struct sigaction s;
memset(&s,0,sizeof(s));
s.sa_handler=sigint_handler;
s.sa_flags=SA_RESTART;
SYSCALL(notused,sigaction(SIGINT,&s,NULL),"SIGINT");
SYSCALL(notused,sigemptyset(&setmask),"SIGEMPTYSET");
SYSCALL(notused,pthread_sigmask(SIG_SETMASK,&setmask,NULL),"sigmask");
int listenfd;
SYSCALL(listenfd, socket(AF_UNIX, SOCK_STREAM, 0), "socket");
struct sockaddr_un serv_addr;
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strncpy(serv_addr.sun_path, UNIXPATH, strlen(UNIXPATH)+1);
SYSCALL(notused, bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr)), "bind");
SYSCALL(notused, listen(listenfd, MAXCONNECTION), "listen");
long fd_c;
int i=0;
fd_set set, rdset;
FD_ZERO(&rdset);
FD_ZERO (&set);
FD_SET(listenfd,&set);
int fd_num=listenfd;
struct timeval tv;
tv.tv_sec=3;
tv.tv_usec=0;
while(1){
if(terminate==1){
break;
}
rdset=set;
if((notused=select(fd_num+1, &rdset, NULL, NULL, &tv))==-1){
if(errno==EINTR){
if(terminate==1){
break;
}else{
perror("select");
exit(EXIT_FAILURE);
}
}
}
for(i=0; i<=fd_num;i++){
if(FD_ISSET(i,&rdset)){
if(i==listenfd){
SYSCALL(fd_c, accept(listenfd, (struct sockaddr*)NULL ,NULL), "accept");
printf("connection accepted\n");
FD_SET(fd_c, &set);
if(fd_c>fd_num) fd_num=fd_c;
}else{
fd_c=i;
}
insert(fd_c);
FD_CLR(fd_c,&set);
if(fd_c==fd_num) fd_num=update(set,fd_num);
}
}
}
pthread_mutex_lock(&mtx);
try=1;
pthread_mutex_unlock(&mtx);
close(listenfd);
pthread_cond_broadcast(&empty);
pthread_cond_signal(&full);
// join on the thread and free(array)
destroyArray(array);
if(tmp)
free(tmp);
return 0;
}
Finally, this is the Valgrind output I sometimes get when I execute the code:
==7578== Memcheck, a memory error detector
==7578== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==7578== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==7578== Command: ./server
==7578==
^Cthread: 100800256 terminated
thread: 109192960 terminated
thread: 117585664 terminated
thread: 125978368 terminated
thread: 134371072 terminated
thread: 142763776 terminated
thread: 151156480 terminated
thread: 159549184 terminated
==7578==
==7578== HEAP SUMMARY:
==7578== in use at exit: 1,638 bytes in 4 blocks
==7578== total heap usage: 15 allocs, 11 frees, 4,958 bytes allocated
==7578==
==7578== 36 bytes in 1 blocks are still reachable in loss record 1 of 4
==7578== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578== by 0x401CF99: strdup (strdup.c:42)
==7578== by 0x40187DE: _dl_load_cache_lookup (dl-cache.c:311)
==7578== by 0x4009168: _dl_map_object (dl-load.c:2364)
==7578== by 0x4015576: dl_open_worker (dl-open.c:237)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x4014DA8: _dl_open (dl-open.c:660)
==7578== by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x519A663: dlerror_run (dl-libc.c:46)
==7578== by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578== by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578== by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578==
==7578== 36 bytes in 1 blocks are still reachable in loss record 2 of 4
==7578== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578== by 0x400BEF3: _dl_new_object (dl-object.c:165)
==7578== by 0x400650C: _dl_map_object_from_fd (dl-load.c:1028)
==7578== by 0x4008C26: _dl_map_object (dl-load.c:2498)
==7578== by 0x4015576: dl_open_worker (dl-open.c:237)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x4014DA8: _dl_open (dl-open.c:660)
==7578== by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x519A663: dlerror_run (dl-libc.c:46)
==7578== by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578== by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578== by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578==
==7578== 384 bytes in 1 blocks are still reachable in loss record 3 of 4
==7578== at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578== by 0x40120BD: _dl_check_map_versions (dl-version.c:293)
==7578== by 0x4015B18: dl_open_worker (dl-open.c:286)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x4014DA8: _dl_open (dl-open.c:660)
==7578== by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x519A663: dlerror_run (dl-libc.c:46)
==7578== by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578== by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578== by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578== by 0x4E4A06F: __pthread_unwind (unwind.c:121)
==7578== by 0x4E42844: __do_cancel (pthreadP.h:283)
==7578== by 0x4E42844: pthread_exit (pthread_exit.c:28)
==7578==
==7578== 1,182 bytes in 1 blocks are still reachable in loss record 4 of 4
==7578== at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578== by 0x400BBF5: _dl_new_object (dl-object.c:75)
==7578== by 0x400650C: _dl_map_object_from_fd (dl-load.c:1028)
==7578== by 0x4008C26: _dl_map_object (dl-load.c:2498)
==7578== by 0x4015576: dl_open_worker (dl-open.c:237)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x4014DA8: _dl_open (dl-open.c:660)
==7578== by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578== by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578== by 0x519A663: dlerror_run (dl-libc.c:46)
==7578== by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578== by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578== by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578==
==7578== LEAK SUMMARY:
==7578== definitely lost: 0 bytes in 0 blocks
==7578== indirectly lost: 0 bytes in 0 blocks
==7578== possibly lost: 0 bytes in 0 blocks
==7578== still reachable: 1,638 bytes in 4 blocks
==7578== suppressed: 0 bytes in 0 blocks
==7578==
==7578== For counts of detected and suppressed errors, rerun with: -v
==7578== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
New Valgrind output
==3920== Memcheck, a memory error detector
==3920== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3920== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==3920== Command: ./server
==3920==
^C
==3920==
==3920== Process terminating with default action of signal 2 (SIGINT)
==3920== at 0x4E3FE6D: __pthread_initialize_minimal (nptl-init.c:433)
==3920== by 0x4E3F588: ??? (in /lib/x86_64-linux-gnu/libpthread-2.23.so)
==3920== by 0x400044F: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==3920== by 0x4010679: call_init.part.0 (dl-init.c:58)
==3920== by 0x4010834: call_init (dl-init.c:104)
==3920== by 0x4010834: _dl_init (dl-init.c:87)
==3920== by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==3920==
==3920== HEAP SUMMARY:
==3920== in use at exit: 0 bytes in 0 blocks
==3920== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3920==
==3920== All heap blocks were freed -- no leaks are possible
==3920==
==3920== For counts of detected and suppressed errors, rerun with: -v
==3920== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
but sometimes it ends without memory leaks, others reporting some memory leaks that I leave behind.
That's probably because the program does not clean up its linked list of accepted connections before terminating.
But you have some other troubling issues in your code.
First, your use of the terminate variable is not thread safe. Do not be confused by the name of type sig_atomic_t: it is not an atomic datatype. It is safe for use by a signal handler to communicate with the thread that received the signal, but it is not safe for communicating between threads without use of an appropriate synchronization object, such as a mutex. Moreover, although necessary for use with a signal handler, making it volatile also fails to confer thread safety. This is a problem for your current code because when you send a SIGINT, it can be handled by any thread.
I suggest resolving this by blocking SIGINT in all but the main thread. When a process receives a process-directed signal, it is queued for a thread that does not at that time have it blocked, so you should not need to worry about successfully receiving the signal. Every thread has its own signal mask, whose initial value is inherited from its parent thread. Therefore, I suggest blocking SIGINT in the main thread before launching all the workers, then unblocking it only in the main thread once the workers are all running.
Second, all accesses to mutable, non-atomic data shared between threads need to protected by an appropriate synchronization object. You have chosen a mutex for that purpose, but your program performs some accesses without its protection.
The shared data include at least variables try, nelem, and all the contents of the linked list headed by head.
threadF() checks try without holding the mutex
main() sets try without holding the mutex
The behavior of your program is therefore undefined. In practice, it seems unlikely that this has anything to do with the "leak", but it might contribute to your program occasionally failing to shut down cleanly. In principle, anything might happen. In practice, the most likely effect is that your program occasionally fails to shut down cleanly.
I'm using pthreads and according to valgrind I am leaking memory, like in valgrind memory leak errors when using pthread_create
The top answer says that if you pthread_join all the threads this memory will be reclaimed, but it isn't for me.
pthread_t threads[NUM_THREADS];
...
for (i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], &attr, Worker, NULL);
}
...
for (i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
valgrind output
==2707== HEAP SUMMARY:
==2707== in use at exit: 954 bytes in 4 blocks
==2707== total heap usage: 7,717 allocs, 7,713 frees, 79,563 bytes allocated
==2707==
==2707== 34 bytes in 1 blocks are still reachable in loss record 1 of 4
==2707== at 0x402BE68: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==2707== by 0x400541E: local_strdup (dl-load.c:162)
==2707== by 0x40085D3: _dl_map_object (dl-load.c:2473)
==2707== by 0x4012D5C: dl_open_worker (dl-open.c:225)
==2707== by 0x400ECBE: _dl_catch_error (dl-error.c:178)
==2707== by 0x4184D40: do_dlopen (dl-libc.c:89)
==2707== by 0x404CD4B: start_thread (pthread_create.c:308)
==2707== by 0x414BACD: clone (clone.S:130)
==2707==
==2707== 34 bytes in 1 blocks are still reachable in loss record 2 of 4
==2707== at 0x402BE68: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==2707== by 0x400B05A: _dl_new_object (dl-object.c:161)
==2707== by 0x4006437: _dl_map_object_from_fd (dl-load.c:1051)
==2707== by 0x400839F: _dl_map_object (dl-load.c:2568)
==2707== by 0x4012D5C: dl_open_worker (dl-open.c:225)
==2707== by 0x400ECBE: _dl_catch_error (dl-error.c:178)
==2707== by 0x4184D40: do_dlopen (dl-libc.c:89)
==2707== by 0x404CD4B: start_thread (pthread_create.c:308)
==2707== by 0x414BACD: clone (clone.S:130)
==2707==
==2707== 256 bytes in 1 blocks are still reachable in loss record 3 of 4
==2707== at 0x402A5E6: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==2707== by 0x401045C: _dl_check_map_versions (dl-version.c:300)
==2707== by 0x401336A: dl_open_worker (dl-open.c:268)
==2707== by 0x400ECBE: _dl_catch_error (dl-error.c:178)
==2707== by 0x4184D40: do_dlopen (dl-libc.c:89)
==2707== by 0x404CD4B: start_thread (pthread_create.c:308)
==2707== by 0x414BACD: clone (clone.S:130)
==2707==
==2707== 630 bytes in 1 blocks are still reachable in loss record 4 of 4
==2707== at 0x402A5E6: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==2707== by 0x400AE0F: _dl_new_object (dl-object.c:77)
==2707== by 0x4006437: _dl_map_object_from_fd (dl-load.c:1051)
==2707== by 0x400839F: _dl_map_object (dl-load.c:2568)
==2707== by 0x4012D5C: dl_open_worker (dl-open.c:225)
==2707== by 0x400ECBE: _dl_catch_error (dl-error.c:178)
==2707== by 0x4184D40: do_dlopen (dl-libc.c:89)
==2707== by 0x404CD4B: start_thread (pthread_create.c:308)
==2707== by 0x414BACD: clone (clone.S:130)
==2707==
==2707== LEAK SUMMARY:
==2707== definitely lost: 0 bytes in 0 blocks
==2707== indirectly lost: 0 bytes in 0 blocks
==2707== possibly lost: 0 bytes in 0 blocks
==2707== still reachable: 954 bytes in 4 blocks
==2707== suppressed: 0 bytes in 0 blocks
==2707==
==2707== For counts of detected and suppressed errors, rerun with: -v
==2707== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Am I doing something wrong or is the top answer incorrect?
This is not an actual leak and you can safely ignore it. The memory block pthread_create allocates is used for extending the thread stack and it is not freed. The library may use the same memory region for possible future calls to pthread_create.
I am probably late to the show. As pointed out, it must not have any memory leak - I checked what you have done it is standard way of creating (create internal memory for thread) and joining a thread (auto reclaims this memory).
However, you have not shown the initialization of attr.
You may try doing:
pthread_create(&threads[i], NULL, Worker, NULL);
I have placed NULL here, instead of any value for attr. The pthread initialization is tightly coupled to attribute (unless it is provided as NULL).