?RCS: $Id$
?RCS:
?RCS: Copyright (c) 2021 Nicholas Clark & H.Merijn Brand
?RCS:
?RCS: You may distribute under the terms of either the GNU General Public
?RCS: License or the Artistic License, as specified in the README file.
?RCS:
?MAKE:d_thread_local perl_thread_local: cat echo Setvar Compile run rm_try hint
?MAKE:	-pick add $@ %<
?S:d_thread_local:
?S:	This variable conditionally defines the PERL_THREAD_LOCAL symbol. In
?S:	turn that gives a linkage specification for thread-local storage.
?S:.
?S:perl_thread_local:
?S:	This variable gives the value for the PERL_THREAD_LOCAL symbol (when
?S:	defined), which gives a linkage specification for thread-local storage.
?S:.
?C:PERL_THREAD_LOCAL:
?C:	This symbol, if defined, gives a linkage specification for thread-local
?C:	storage. For example, for a C11 compiler this will be _Thread_local.
?C:	Beware, some compilers are sensitive to the C language standard they are
?C:	told to parse. For example, suncc defaults to C11, so our probe will
?C:	report that _Thread_local can be used. However, if the -std=c99 is later
?C:	added to the compiler flags, then _Thread_local will become a syntax
?C:	error. Hence it is important for these flags to be consistent between
?C:	probing and use.
?C:.
?H:#$d_thread_local PERL_THREAD_LOCAL $perl_thread_local	/**/
?H:.
?T:thread_local
?F:!try
?LINT:set d_thread_local
: see what flavor, if any, of thread local storage is supported
echo " "
echo "Checking to see if your system supports C11 thread local storage..."
$cat > try.c <<'EOCP'
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static int plus_one = 1;
static int minus_one = -1;

PROBE_MACRO 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;
}
EOCP

# Respect a hint (or previous) value for perl_thread_local, if there is one.
case "$perl_thread_local" in
'')	# Check the various possibilities, and break out on success.
	for thread_local in _Thread_local __thread; do
		set try -DPROBE_MACRO=$thread_local
		if eval $compile && $run ./try; then
			$echo "Your compiler supports $thread_local." >&4
			val=$define
			perl_thread_local="$thread_local";
			break;
		fi
		$echo "Your compiler does NOT support $thread_local." >&4
		val="$undef"
	done
	;;
*thread*|*Thread*) # Some variant of thread local exists.
	echo "Keeping your $hint value of $perl_thread_local."
	val=$define
	;;
*)  # Unrecognized previous value -- blindly trust the supplied
	# value and hope it makes sense.  Use old value for
	# d_thread_local, if there is one.
	echo "Keeping your $hint value of $perl_thread_local."
	case "$d_thread_local" in
		'') val=$define ;;
		*)  val=$d_thread_local ;;
	esac
	;;
esac
set d_thread_local
eval $setvar
$rm_try

