Weird threading / __thread behavior

Zach Saw zach.saw@gmail.com
Mon Feb 18 03:38:00 GMT 2013


The following test case fails on Cygwin but passes on Linux (both tested using 
GCC 4.7.2). There are two failures that this test case exposes -- first without 
commenting out the line "test = true;" and the second is with it commented out. 
The first case shows the variable 'test' is not being treated as a threadlocal 
var. In the second case, test will pass but fail sporadically on multi-core 
machines. If you mask CPU affinity out to enable just one core, it will pass 
100% of the time. It shows a separate problem to case #1 (i.e. multithreaded 
apps will not run reliably on Cygwin). On my machine (Core2Duo), it fails on 
average once out of 5 runs.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.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);                                     \
 }                                              \
}
 
__thread int TLS_data1 = 0;
__thread int TLS_data2 = 0;
__thread bool test = false;
 
#define                 NUMTHREADS   20 

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

threadparm_t          gResults[NUMTHREADS];

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

   gData = (threadparm_t *)parm;
   sleep(1);
   if (!test)
   {
      test = true; // *** comment this out for test #2
      TLS_data1 = gData->data1;
      TLS_data2 = gData->data2;
   }

   foo();
   return NULL;
}
 
void foo() {
   gResults[TLS_data1].data1 = TLS_data1;
   bar();
}
 
void bar() {
   gResults[TLS_data1].data2 = TLS_data2;
}

int main(int argc, char **argv)
{
  pthread_t             thread[NUMTHREADS];
  int                   rc=0;
  int                   i;
  threadparm_t          gData[NUMTHREADS];
 
  printf("Enter Testcase - %s\n", argv[0]);
 
  for (i=0; i < NUMTHREADS; i++) {
     gResults[i].data1 = 0;
     gResults[i].data2 = 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;
     printf("%d %d\n", gData[i].data1, gData[i].data2);
     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);
  }

  for (i=0; i < NUMTHREADS; i++) {
     printf("gResults, data=%d %d\n",
          gResults[i].data1, gResults[i].data2);
     if (gResults[i].data1 != gData[i].data1)
         printf("failed\n");
     if (gResults[i].data2 != gData[i].data2)
         printf("failed\n");
  }
  
  printf("Main completed\n");
  return 0;
}



Compile with: g++ tls_test.cpp -o tls_test.exe -lpthread

Expected run outcome: no "failed" messages.


--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple



More information about the Cygwin mailing list