From 8d68b18f9964be8be7ffe1ed18074eae7dc3c021 Mon Sep 17 00:00:00 2001 From: Nicholas Clark Date: Sat, 31 Oct 2020 11:23:29 +0000 Subject: [PATCH] A probe for compiler thread local storage support. Not needed on Win32, as UV already exposes it there. --- Configure.pl | 2 + build/config.h.in | 5 ++ build/probe.pm | 126 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) diff --git a/Configure.pl b/Configure.pl index f045a81e9d..29b00e190a 100755 --- a/Configure.pl +++ b/Configure.pl @@ -490,12 +490,14 @@ sub uniq { if ($config{crossconf}) { build::auto::detect_cross(\%config, \%defaults); build::probe::static_inline_cross(\%config, \%defaults); + build::probe::thread_local_cross(\%config, \%defaults); build::probe::unaligned_access_cross(\%config, \%defaults); build::probe::ptr_size_cross(\%config, \%defaults); } else { build::auto::detect_native(\%config, \%defaults); build::probe::static_inline_native(\%config, \%defaults); + build::probe::thread_local_native(\%config, \%defaults); build::probe::unaligned_access(\%config, \%defaults); build::probe::ptr_size_native(\%config, \%defaults); } diff --git a/build/config.h.in b/build/config.h.in index 658624b1bf..b16f39b27e 100644 --- a/build/config.h.in +++ b/build/config.h.in @@ -46,6 +46,11 @@ /* How this compiler does static inline functions. */ #define MVM_STATIC_INLINE @static_inline@ +#if @has_thread_local@ +/* How this compiler declares thread local storage. */ +#define MVM_THREAD_LOCAL @thread_local@ +#endif + #if @can_unaligned_int32@ #define MVM_CAN_UNALIGNED_INT32 #endif diff --git a/build/probe.pm b/build/probe.pm index 7e6bfb92fe..7dc5440b7d 100644 --- a/build/probe.pm +++ b/build/probe.pm @@ -227,6 +227,132 @@ sub static_inline_cross { $config->{static_inline} = 'static'; } +sub thread_local_cross { + my ($config) = @_; + $config->{has_thread_local} = 0; + $config->{thread_local} = ""; +} + +sub thread_local_native { + my ($config) = @_; + + # We don't need to probe for this on Win32, as UV sets it for us + if ($^O eq 'MSWin32') { + $config->{has_thread_local} = 0; + $config->{thread_local} = ""; + return; + } + + my $restore = _to_probe_dir(); + + print ::dots(' probing if your compiler offers thread local storage'); + my $file = 'thread_local.c'; + _spew($file, <<'EOT'); +#include +#include +#include + +static int plus_one = 1; +static int minus_one = -1; + +THREAD_LOCAL int *minion; + +int callback (const void *a, const void *b) { + int val_a = *minion * *(const int *)a; + int val_b = *minion * *(const int *)b; + return val_a < val_b ? -1 : val_a > val_b; +} + +#define SIZE 8 + +void *thread_function(void *arg) { + /* thread local variables should start zeroed in each thread. */ + if (minion != NULL) { + fprintf(stderr, "__thread variable started with %p, should be NULL\n", + minion); + exit(2); + } + minion = &minus_one; + + int array[SIZE]; + unsigned int i; + for (i = 0; i < SIZE; ++i) { + /* "Hash randomisation" - this array isn't in sorted order: */ + array[i ^ 5] = i * i; + } + + qsort(array, SIZE, sizeof(int), callback); + + int bad = 0; + for (i = 0; i < SIZE; ++i) { + int want = (SIZE - 1 - i) * (SIZE - 1 - i); + int have = array[i]; + if (want != have) { + ++bad; + fprintf(stderr, "array[%u] - want %i, have %i\n", i, want, have); + } + } + if (bad) + exit(3); + + return NULL; +} + +int main(int argc, char **argv) { + if (minion != NULL) { + fprintf(stderr, "__thread variable started with %p, should be NULL\n", + minion); + exit(4); + } + + minion = &plus_one; + + pthread_t tid; + int result = pthread_create(&tid, NULL, thread_function, NULL); + if (result) { + fprintf(stderr, "pthread_create failed (%d)\n", result); + exit(5); + } + + result = pthread_join(tid, NULL); + if (result) { + fprintf(stderr, "pthread_join failed (%d)\n", result); + exit(6); + } + + if (minion == NULL) { + fprintf(stderr, "__thread variable should not be NULL\n"); + exit(7); + } + if (!(minion == &plus_one && *minion == 1)) { + fprintf(stderr, "__thread variable should be %d @ %p, not %d @ %p\n", + 1, &plus_one, *minion, minion); + exit(8); + } + + return 0; +} +EOT + + my @try = qw(_Thread_local __thread); + + my $t_l; + while ($t_l = shift @try) { + next unless compile($config, 'thread_local', ["THREAD_LOCAL=$t_l"]); + last if !system "./thread_local"; + } + + if ($t_l) { + print "$t_l\n"; + $config->{has_thread_local} = 1; + $config->{thread_local} = $t_l; + } else { + print "it doesn't, so falling back to UV's API\n"; + $config->{has_thread_local} = 0; + $config->{thread_local} = ""; + } +} + sub specific_werror { my ($config) = @_; my $restore = _to_probe_dir();