From a5d465029ce1ffce4860c75c2aa3fa0d1a8e18be Mon Sep 17 00:00:00 2001 From: Anthony Green Date: Sun, 8 Jun 2025 07:40:39 -0400 Subject: [PATCH] feat(testsuite): add new threading tests to libffi test suite --- testsuite/libffi.threads/ffitest.h | 163 +++++++++++++++++++++++++++ testsuite/libffi.threads/threads.exp | 54 +++++++++ testsuite/libffi.threads/tsan.c | 66 +++++++++++ 3 files changed, 283 insertions(+) create mode 100644 testsuite/libffi.threads/ffitest.h create mode 100644 testsuite/libffi.threads/threads.exp create mode 100644 testsuite/libffi.threads/tsan.c diff --git a/testsuite/libffi.threads/ffitest.h b/testsuite/libffi.threads/ffitest.h new file mode 100644 index 00000000..8bd8a3cb --- /dev/null +++ b/testsuite/libffi.threads/ffitest.h @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include "fficonfig.h" + +#include +#include + +#if defined HAVE_STDINT_H +#include +#endif + +#if defined HAVE_INTTYPES_H +#include +#endif + +#define MAX_ARGS 256 + +#define CHECK(x) \ + do { \ + if(!(x)){ \ + printf("Check failed:\n%s\n", #x); \ + abort(); \ + } \ + } while(0) + +#define CHECK_FLOAT_EQ(x, y) \ + do { \ + if(fabs((x) - (y)) > FLT_EPSILON){ \ + printf("Check failed CHECK_FLOAT_EQ(%s, %s)\n", #x, #y); \ + abort(); \ + } \ + } while(0) + +#define CHECK_DOUBLE_EQ(x, y) \ + do { \ + if(fabs((x) - (y)) > DBL_EPSILON){ \ + printf("Check failed CHECK_FLOAT_EQ(%s, %s)\n", #x, #y); \ + abort(); \ + } \ + } while(0) + +/* Define macros so that compilers other than gcc can run the tests. */ +#undef __UNUSED__ +#if defined(__GNUC__) +#define __UNUSED__ __attribute__((__unused__)) +#define __STDCALL__ __attribute__((stdcall)) +#define __THISCALL__ __attribute__((thiscall)) +#define __FASTCALL__ __attribute__((fastcall)) +#define __MSABI__ __attribute__((ms_abi)) +#else +#define __UNUSED__ +#define __STDCALL__ __stdcall +#define __THISCALL__ __thiscall +#define __FASTCALL__ __fastcall +#endif + +#ifndef ABI_NUM +#define ABI_NUM FFI_DEFAULT_ABI +#define ABI_ATTR +#endif + +/* Prefer MAP_ANON(YMOUS) to /dev/zero, since we don't need to keep a + file open. */ +#ifdef HAVE_MMAP_ANON +# undef HAVE_MMAP_DEV_ZERO + +# include +# ifndef MAP_FAILED +# define MAP_FAILED -1 +# endif +# if !defined (MAP_ANONYMOUS) && defined (MAP_ANON) +# define MAP_ANONYMOUS MAP_ANON +# endif +# define USING_MMAP + +#endif + +#ifdef HAVE_MMAP_DEV_ZERO + +# include +# ifndef MAP_FAILED +# define MAP_FAILED -1 +# endif +# define USING_MMAP + +#endif + +/* msvc kludge. */ +#if defined(_MSC_VER) +#define PRIdLL "I64d" +#define PRIuLL "I64u" +#else +#define PRIdLL "lld" +#define PRIuLL "llu" +#endif + +/* Tru64 UNIX kludge. */ +#if defined(__alpha__) && defined(__osf__) +/* Tru64 UNIX V4.0 doesn't support %lld/%lld, but long is 64-bit. */ +#undef PRIdLL +#define PRIdLL "ld" +#undef PRIuLL +#define PRIuLL "lu" +#define PRId8 "hd" +#define PRIu8 "hu" +#define PRId64 "ld" +#define PRIu64 "lu" +#define PRIuPTR "lu" +#endif + +/* PA HP-UX kludge. */ +#if defined(__hppa__) && defined(__hpux__) && !defined(PRIuPTR) +#define PRIuPTR "lu" +#endif + +/* IRIX kludge. */ +#if defined(__sgi) +/* IRIX 6.5 provides all definitions, but only for C99 + compilations. */ +#define PRId8 "hhd" +#define PRIu8 "hhu" +#if (_MIPS_SZLONG == 32) +#define PRId64 "lld" +#define PRIu64 "llu" +#endif +/* This doesn't match , which always has "lld" here, but the + arguments are uint64_t, int64_t, which are unsigned long, long for + 64-bit in . */ +#if (_MIPS_SZLONG == 64) +#define PRId64 "ld" +#define PRIu64 "lu" +#endif +/* This doesn't match , which has "u" here, but the arguments + are uintptr_t, which is always unsigned long. */ +#define PRIuPTR "lu" +#endif + +/* Solaris < 10 kludge. */ +#if defined(__sun__) && defined(__svr4__) && !defined(PRIuPTR) +#if defined(__arch64__) || defined (__x86_64__) +#define PRIuPTR "lu" +#else +#define PRIuPTR "u" +#endif +#endif + +/* MSVC kludge. */ +#if defined _MSC_VER +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) +#define PRIuPTR "lu" +#define PRIu8 "u" +#define PRId8 "d" +#define PRIu64 "I64u" +#define PRId64 "I64d" +#endif +#endif + +#ifndef PRIuPTR +#define PRIuPTR "u" +#endif diff --git a/testsuite/libffi.threads/threads.exp b/testsuite/libffi.threads/threads.exp new file mode 100644 index 00000000..13ba2bdc --- /dev/null +++ b/testsuite/libffi.threads/threads.exp @@ -0,0 +1,54 @@ +# Copyright (C) 2003, 2006, 2009, 2010, 2014 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING3. If not see +# . + +dg-init +libffi-init + +global srcdir subdir + +if { [string match $compiler_vendor "microsoft"] } { + # -wd4005 macro redefinition + # -wd4244 implicit conversion to type of smaller size + # -wd4305 truncation to smaller type + # -wd4477 printf %lu of uintptr_t + # -wd4312 implicit conversion to type of greater size + # -wd4311 pointer truncation to unsigned long + # -EHsc C++ Exception Handling (no SEH exceptions) + set additional_options "-wd4005 -wd4244 -wd4305 -wd4477 -wd4312 -wd4311 -EHsc"; +} else { + set additional_options ""; +} + +set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.c]] + +run-many-tests $tlist $additional_options + +set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.cc]] + +# No C++ for or1k +if { [istarget "or1k-*-*"] } { + foreach test $tlist { + unsupported "$test" + } +} else { + run-many-tests $tlist $additional_options +} + +dg-finish + +# Local Variables: +# tcl-indent-level:4 +# End: diff --git a/testsuite/libffi.threads/tsan.c b/testsuite/libffi.threads/tsan.c new file mode 100644 index 00000000..d6134f85 --- /dev/null +++ b/testsuite/libffi.threads/tsan.c @@ -0,0 +1,66 @@ +/* { dg-do run } */ + +#include "ffitest.h" + +#include +#include +#include + +#define NUM_THREADS 20 + +pthread_barrier_t barrier; + +typedef float (*callback_fn)(float, float); + +void callback(ffi_cif *cif __UNUSED__, void *ret, void **args, void *userdata __UNUSED__) { + float a = *(float *)args[0]; + float b = *(float *)args[1]; + *(float *)ret = a / 2 + b / 2; +} + +void *thread_func(void *arg) { + pthread_barrier_wait(&barrier); + + ffi_cif cif; + ffi_type *args[2] = { &ffi_type_float, &ffi_type_float }; + + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_float, args) != FFI_OK) { + fprintf(stderr, "ffi_prep_cif failed\n"); + return NULL; + } + + ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), (void **)&arg); + + if (ffi_prep_closure_loc(closure, &cif, callback, NULL, arg) != FFI_OK) { + fprintf(stderr, "ffi_prep_closure_loc failed\n"); + return NULL; + } + + callback_fn fn = (callback_fn)arg; + (void) fn(4.0f, 6.0f); + + ffi_closure_free(closure); + return NULL; +} + +int main() { + pthread_t threads[NUM_THREADS]; + + pthread_barrier_init(&barrier, NULL, NUM_THREADS); + + for (int i = 0; i < NUM_THREADS; ++i) { + if (pthread_create(&threads[i], NULL, thread_func, NULL) != 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + } + + for (int i = 0; i < NUM_THREADS; ++i) { + pthread_join(threads[i], NULL); + } + + pthread_barrier_destroy(&barrier); + + printf("Completed\n"); + return 0; +}