0

Under Macos, program compiled by apple clang can't read the latest TLS data in pthread_key_create destructor, while if it is compiled by gcc at the same environment, it works as expected.

Here is a self-contained example:

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

void foo(void);  /* Functions that use the TLS data */
void bar(void);

#define checkResults(string, val) {             \
 if (val) {                                     \
   printf("Failed with %d at %s", val, string); \
   exit(1);                                     \
 }                                              \
}
#define                 NUMTHREADS   2

static pthread_key_t s_key;

__thread int TLS_data1;
__thread int TLS_data2;

void dtor(void* args) {
    printf("In dtor, TLS_data1=%d, TLS_data2=%d\n", TLS_data1, TLS_data2);
}


typedef struct {
   int   data1;
   int   data2;
} threadparm_t;

void *theThread(void *parm)
{
   int               rc;
   threadparm_t     *gData;

   pthread_setspecific(s_key, parm);

   printf("Thread %.16llx: Entered\n", pthread_self());

   gData = (threadparm_t *)parm;

   TLS_data1 = gData->data1;
   TLS_data2 = gData->data2;

   foo();
   return NULL;
}

void foo() {
   printf("Thread %.16llx: foo(), TLS data=%d %d\n",
          pthread_self(), TLS_data1, TLS_data2);
   bar();
}

void bar() {
   printf("Thread %.16llx: bar(), TLS data=%d %d\n",
          pthread_self(), TLS_data1, TLS_data2);
   return;
}

int main(int argc, char **argv)
{
  pthread_t             thread[NUMTHREADS];
  int                   rc=0;
  int                   i;
  threadparm_t          gData[NUMTHREADS];

  pthread_key_create(&s_key, dtor);

  printf("Enter Testcase - %s\n", argv[0]);

  printf("Create/start threads\n");
  for (i=0; i < NUMTHREADS; i++) {
     /* Create per-thread TLS data and pass it to the thread */
     gData[i].data1 = i;
     gData[i].data2 = (i+1)*2;
     rc = pthread_create(&thread[i], NULL, theThread, &gData[i]);
     checkResults("pthread_create()\n", rc);
  }

  printf("Wait for the threads to complete, and release their resources\n");
  for (i=0; i < NUMTHREADS; i++) {
     rc = pthread_join(thread[i], NULL);
     checkResults("pthread_join()\n", rc);
  }

  printf("Main completed\n");
  return 0;
}

In macos, use command clang example.c to compile the program and run it, you can get following result(My clang version is Apple LLVM version 8.0.0 (clang-800.0.42.1)):

Enter Testcase - ./a.out
Create/start threads
Wait for the threads to complete, and release their resources
Thread 0000700000081000: Entered
Thread 0000700000104000: Entered
Thread 0000700000104000: foo(), TLS data=1 4
Thread 0000700000104000: bar(), TLS data=1 4
In dtor, TLS_data1=0, TLS_data2=0
Thread 0000700000081000: foo(), TLS data=0 2
Thread 0000700000081000: bar(), TLS data=0 2
In dtor, TLS_data1=0, TLS_data2=0
Main completed

You can see that in dtor the tls can not be read correctly, then use gcc instead(under macos or linux),you can get the result:

Enter Testcase - ./a.out
Create/start threads
Wait for the threads to complete, and release their resources
Thread 0000700000081000: Entered
Thread 0000700000104000: Entered
Thread 0000700000104000: foo(), TLS data=1 4
Thread 0000700000104000: bar(), TLS data=1 4
In dtor, TLS_data1=1, TLS_data2=4
Thread 0000700000081000: foo(), TLS data=0 2
Thread 0000700000081000: bar(), TLS data=0 2
In dtor, TLS_data1=0, TLS_data2=2
Main completed

Now, the tls data can be read correctly by destructor. My question is how to set apple clang compiler to make the result right?

zjs
  • 43
  • 2
  • Why are you using a big, ugly `#define` in C++ when there's no reason for that? A regular function will suffice here. This code would also be considerably less messy without clunky thread globals. – tadman Mar 26 '18 at 03:27
  • Does using the C++11 [`thread_local`](http://en.cppreference.com/w/cpp/language/storage_duration) specifier instead of `__thread` fix the problem in clang? – tadman Mar 26 '18 at 03:35
  • Are you programming in C or C++? If it's C, the question isn't tagged correctly. – MrEricSir Mar 26 '18 at 03:40
  • No, it doesn't work @tadman – zjs Mar 26 '18 at 04:02
  • Thanks, I changed the tag @MrEricSir – zjs Mar 26 '18 at 05:07
  • So, if it's C, why do you talk about destructor? – jwdonahue Mar 26 '18 at 05:40
  • Check your clang docs, does it really support thread local storage? – jwdonahue Mar 26 '18 at 05:41
  • 1
    Possible duplicate of [No luck compiling \_\_thread using ndk clang 3.4/3.5](https://stackoverflow.com/questions/27191214/no-luck-compiling-thread-using-ndk-clang-3-4-3-5) – jwdonahue Mar 26 '18 at 05:43
  • The term 'destructor' is referenced from man page of pthread_create_key. My clang version is 8.0.0 and it does support thread local, you can see it from the output begin with "foo" and "bar" @jwdonahue – zjs Mar 26 '18 at 06:47
  • Why are those values `0` in the clang version? They're never initialized is my guess. Debug and step through your `theThread` function. – tadman Mar 26 '18 at 15:10

0 Answers0