> I completely avoid the need for TLS and mutexes in the code I write,
Sounds good so far.
But is it genuinely thread safe?
Do you for example register functions with these 'thread using libraries' that result in your code being called in some kind of thread context?
> but I'm adopting 3rd-party code that requires TLS and mutexes
So why do you care?
I mean, all you do is follow the instructions for said 3rd party library when it says "compile and link with -pthread" or "compile with C11 or newer compiler".
Your code doesn't care, and you get to treat the library as a black box.
Even if you have two libs using two different approaches, it should still be fine.
Each should be internally consistent in their respective use of threads, locks, storage etc.
Code:
#include <stdio.h>
#include <threads.h> // C11 threads
#include <pthread.h> // Posix threads
#include <unistd.h>
thread_local int thread_local_var;
int thread_fn(void *p) {
int instance = *(int*)p;
printf("T[%d] hello from thread_fn\n",instance);
printf("T[%d] getpid=%d\n",instance,getpid());
printf("T[%d] thrd_current=%lx\n",instance,(unsigned long)thrd_current());
printf("T[%d] ptr to tls=%p\n",instance,(void*)&thread_local_var);
return 0;
}
__thread int pthread_local_var;
void *pthread_fn(void *p) {
int instance = *(int*)p;
printf("P[%d] hello from pthread_fn\n",instance);
printf("P[%d] getpid=%d\n",instance,getpid());
printf("P[%d] pthread_self=%lx\n",instance,(unsigned long)pthread_self());
printf("P[%d] ptr to tls=%p\n",instance,(void*)&pthread_local_var);
return NULL;
}
int main ( ) {
int instances[] = { 1, 2 };
#ifdef USE_C11
thrd_t t1, t2;
int r1 = thrd_create(&t1, thread_fn, &instances[0]);
int r2 = thrd_create(&t2, thread_fn, &instances[1]);
#endif
#ifdef USE_PT
pthread_t p1, p2;
int x1 = pthread_create(&p1, NULL, pthread_fn, &instances[0]);
int x2 = pthread_create(&p2, NULL, pthread_fn, &instances[1]);
#endif
#ifdef USE_C11
int r3 = thrd_join(t1, NULL);
int r4 = thrd_join(t2, NULL);
#endif
#ifdef USE_PT
int x3 = pthread_join(p1, NULL);
int x4 = pthread_join(p2, NULL);
#endif
#ifdef USE_C11
printf("Thread results %d %d %d %d\n", r1, r2, r3, r4);
#endif
#ifdef USE_PT
printf("Pthread results %d %d %d %d\n", x1, x2, x3, x4);
#endif
return 0;
}
Compile with one of
gcc -DUSE_C11 -g -pthread thread_pthread.c
gcc -DUSE_PT -g -pthread thread_pthread.c
gcc -DUSE_PT -DUSE_C11 -g -pthread thread_pthread.c
> And, what about mutexes? Will a C11 mutex work also if somebody uses your code inside OpenMP or pthreads?
As I said, internal consistency is key.
For example, if you call mtx_lock, make damn sure you call mtx_unlock and not pthread_mutex_unlock.
The compiler would normally warn you, unless crippled with brute force casting.
The obvious close alignment of the organisation of the C11 threads and pthreads APIs suggests that the respective standards organisations want people to have an easy migration path.
Sure there may be some implementation wrinkles in some obscure corners, but it's not like they're going out of their way to make them gratuitously incompatible with one another.