Search code examples
cmemory-leakspthreadsvalgrind

pthread_create memory leak in valgrind


Here is a simpler version of my code:

#include <pthread.h>
#include <stdio.h>

void* handle_client(void* arg);

int main(int argc, char *argv[])
{
    pthread_t pthr_handle;
    pthread_create(&pthr_handle, NULL, &handle_client, NULL);
    pthread_detach(pthr_handle);
    pthread_exit(NULL);
    return 0;
}

void* handle_client(void* arg)
{
    printf("Hello from thread!\n");
    pthread_exit(NULL);
    return NULL;
}

When I use valgrind on this program, it says

possibly lost: 272 bytes in 1 blocks

The thing is, it doesn't say that every time I run it. Sometimes it says there are no leaks. Because of that I believe there are no leaks, and the message has something to do with the thread still running after the main thread exits. Isn't the pthread_exit call supposed to make the main thread wait for the other threads to exit? Is there something I can do to make valgrind stop accusing memory leaks?


Solution

  • Valgrind is unable to handle the cleanup that a detached thread performs when it exits after the main thread.

    When you run valgrind, add the --gen-suppressions=yes flag:

    [dbush@db-centos7 ~]$ valgrind --leak-check=full --gen-suppressions=yes ./x1
    ==18866== Memcheck, a memory error detector
    ==18866== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==18866== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==18866== Command: ./x1
    ==18866== 
    Hello from thread!
    ==18866== 
    ==18866== HEAP SUMMARY:
    ==18866==     in use at exit: 560 bytes in 1 blocks
    ==18866==   total heap usage: 6 allocs, 5 frees, 2,192 bytes allocated
    ==18866== 
    ==18866== 560 bytes in 1 blocks are possibly lost in loss record 1 of 1
    ==18866==    at 0x4C2C089: calloc (vg_replace_malloc.c:762)
    ==18866==    by 0x4012784: _dl_allocate_tls (in /usr/lib64/ld-2.17.so)
    ==18866==    by 0x4E3F87B: pthread_create@@GLIBC_2.2.5 (in /usr/lib64/libpthread-2.17.so)
    ==18866==    by 0x400742: main (x1.c:12)
    ==18866== 
    ==18866== 
    ==18866== ---- Print suppression ? --- [Return/N/n/Y/y/C/c] ---- y
    {
       <insert_a_suppression_name_here>
       Memcheck:Leak
       match-leak-kinds: possible
       fun:calloc
       fun:_dl_allocate_tls
       fun:pthread_create@@GLIBC_2.2.5
       fun:main
    }
    ==18866== LEAK SUMMARY:
    ==18866==    definitely lost: 0 bytes in 0 blocks
    ==18866==    indirectly lost: 0 bytes in 0 blocks
    ==18866==      possibly lost: 560 bytes in 1 blocks
    ==18866==    still reachable: 0 bytes in 0 blocks
    ==18866==         suppressed: 0 bytes in 0 blocks
    ==18866== 
    ==18866== For lists of detected and suppressed errors, rerun with: -s
    ==18866== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
    

    In the above output, if you respond "Y" to print suppression, it generates a suppression record. You can add this record to a file (adding a name where it says "<insert_a_suppression_name_here>"), then you can pass this file to valgrind using the --suppressions flag:

    [dbush@db-centos7 ~]$ valgrind  --suppressions=sup1 ./x1
    ==18899== Memcheck, a memory error detector
    ==18899== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==18899== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==18899== Command: ./x1
    ==18899== 
    Hello from thread!
    ==18899== 
    ==18899== HEAP SUMMARY:
    ==18899==     in use at exit: 560 bytes in 1 blocks
    ==18899==   total heap usage: 6 allocs, 5 frees, 2,192 bytes allocated
    ==18899== 
    ==18899== LEAK SUMMARY:
    ==18899==    definitely lost: 0 bytes in 0 blocks
    ==18899==    indirectly lost: 0 bytes in 0 blocks
    ==18899==      possibly lost: 0 bytes in 0 blocks
    ==18899==    still reachable: 0 bytes in 0 blocks
    ==18899==         suppressed: 560 bytes in 1 blocks
    ==18899== 
    ==18899== For lists of detected and suppressed errors, rerun with: -s
    ==18899== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    

    Now it shows as a "suppressed" leak, i.e. one you know about and can safely ignore.