8

I have been experimenting with thread local storage lately. I have working code and every thing seems to be fine but when I run my program with valgrind it looks like there are some issues.

My question is if I allocate memory to static thread local storage will it be deleted upon thread exit ?

Here is my code:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <pthread.h>


void *test (void *arg) 
{
    static __thread int val = 0;
    static __thread char *string = NULL;

    string = (char *) calloc (100, sizeof (char));

    strcpy (string, "hello");

    val++;

    printf ("val(%p):%d\n", &val, val);
    printf ("string(%p):%s\n", &string, string);

    pthread_exit (NULL);
}


int main (int argc, char *argv[])
{
    int num_threads = 10, i;
    pthread_t tid[num_threads];

    for (i=0;i<num_threads;i++) {
        pthread_create (&tid[i], NULL, &test, NULL);
    }

    for (i=0;i<num_threads;i++) {
        pthread_join (tid[i], NULL);
    }

    return 0;
}

output:

val(0x7122b8c):1
string(0x7122b88):hello
val(0x7b23b8c):1
string(0x7b23b88):hello
val(0x924ab8c):1
string(0x924ab88):hello
val(0x9c4bb8c):1
string(0x9c4bb88):hello
val(0xa64cb8c):1
string(0xa64cb88):hello
val(0xb04db8c):1
string(0xb04db88):hello
val(0xba4eb8c):1
string(0xba4eb88):hello
val(0xc44fb8c):1
string(0xc44fb88):hello
val(0xce50b8c):1
string(0xce50b88):hello
val(0xd851b8c):1
string(0xd851b88):hello

valgrind:

==9366== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 41 from 1)
==9366== malloc/free: in use at exit: 1,916 bytes in 15 blocks.
==9366== malloc/free: 70 allocs, 55 frees, 11,218 bytes allocated.
==9366== For counts of detected errors, rerun with: -v
==9366== searching for pointers to 15 not-freed blocks.
==9366== checked 335,336 bytes.
==9366== 
==9366== 1,000 bytes in 10 blocks are definitely lost in loss record 6 of 6
==9366==    at 0x43BB6FF: calloc (vg_replace_malloc.c:279)
==9366==    by 0x80485CD: test (pthread_test.c:14)
==9366==    by 0x51C73A: start_thread (in /lib/libpthread-2.5.so)
==9366==    by 0x4A0CFD: clone (in /lib/libc-2.5.so)
==9366== 
==9366== LEAK SUMMARY:
==9366==    definitely lost: 1,000 bytes in 10 blocks.
==9366==      possibly lost: 0 bytes in 0 blocks.
==9366==    still reachable: 916 bytes in 5 blocks.
==9366==         suppressed: 0 bytes in 0 blocks.
==9366== Reachable blocks (those to which a pointer was found) are not shown.
==9366== To see them, rerun with: --show-reachable=yes
ϹοδεMεδιϲ
  • 2,790
  • 3
  • 33
  • 54
user2962313
  • 89
  • 1
  • 4
  • Thread local simply means that each thread has it's own variable, nothing more. It would be like creating an associative array of threads to values, and it has no intrinsic knowledge of what happens with the values of those variables. – Richard J. Ross III Nov 06 '13 at 21:32
  • 1
    Funny, but I couldn't reproduce the behavior on Ubuntu 13.04. `➜ ~ gcc test.c -o test -ggdb -lpthread; ➜ ~ valgrind test` gives zero errors. – Michael Pankov Nov 06 '13 at 21:44
  • try valgrind --leak-check=full test – user2962313 Nov 06 '13 at 21:50
  • Same. And moreover, it reports only 45 `malloc/free`s. But program runs ok and output is mostly as in your case (although is reordered sometimes). From looking at the backtrace you're running `libc-2.5`, which is pretty much very old. The behavior probably changed in newer `libc`. – Michael Pankov Nov 06 '13 at 21:52
  • libc.so.6 (GLIBC_2.0) => /lib/libc.so.6 CentOS release 5.4 (Final) – user2962313 Nov 06 '13 at 21:57
  • `libc-2.5` is at least 6 years old (2.5 was released in 2006 and minor updated versions were out by 2007). What is the reason to use such an outdated C library? – Michael Pankov Nov 06 '13 at 22:00
  • Wow. I've been able to reproduce the leak only when compiled with `-pthread` instead of `-lpthread`. Look at this question for details on difference http://stackoverflow.com/questions/2127797/gcc-significance-of-pthread-flag-when-compiling – Michael Pankov Nov 06 '13 at 22:04

3 Answers3

6

It will not be automatically deleted upon thread exit. You've declared a pointer in thread local storage. the runtime framework doesn't know how you intend to use that object, so it cannot assume that memory was dynamically allocated.

You are required to free that memory on your own.

Chad
  • 18,706
  • 4
  • 46
  • 63
1

Yes, you should free before the pthread_exit. pthread_exit does not free automatically.

free(string);

should do.

Arjen
  • 636
  • 1
  • 7
  • 13
0

The thread local variable is deleted, but as for any pointer variable, the object it points to is not deallocated automatically: C has nothing like the C++ concept of destructors.

__thread is an extension by gcc, but which corresponds to a new feature, _Threadlocal, of the new C11 standard.

If you are with POSIX for multithreading, there is a similar thing called pthread_getspecific operating on "keys" that lets you design thread local objects. It has the advantage that this has a concept of a destructor function which you can associate to a "key"/

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • "Has nothing" is a little harsh. There is the concept of the 'automatic' memory, which cleans up memory when a scope exits. It doesn't let you hook into that code, but it is automatically reclaimed onto the stack. – Richard J. Ross III Nov 06 '13 at 21:34
  • @RichardJ.RossIII, reclaiming memory and actually executing specific code when a variable falls out of scope, I find these really quite different concepts. – Jens Gustedt Nov 06 '13 at 21:39
  • However, in this scenario all he needs is for the memory to be reclaimed - nothing quite as fancy as C++ destructors, so automatic memory is probably what is causing the confusion. – Richard J. Ross III Nov 06 '13 at 21:41
  • So unless i keep track of allocated memory there's no other way to free it. Thats a bummer – user2962313 Nov 06 '13 at 21:49
  • @user2962313, with `__thread` or `_Treadlocal`, yes, there is no other way. If you were using the POSIX feature, there is. Just use `free` as the function that will be called on the desctruction of the thread "specific". – Jens Gustedt Nov 06 '13 at 22:02
  • @RichardJ.RossIII, he needs the memory of the object the variable is pointing to reclaimed, not the memory of the variable itself. These are quite different things. There is no way a compiler by himself can know that it is ok to free the object, the pointer variable is pointing to. That is a design decision. – Jens Gustedt Nov 06 '13 at 22:04