diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:19:15 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:19:15 +0000 |
commit | 6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch) | |
tree | 657d8194422a5daccecfd42d654b8a245ef7b4c8 /src/test/thread | |
parent | Initial commit. (diff) | |
download | postgresql-13-upstream.tar.xz postgresql-13-upstream.zip |
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/thread')
-rw-r--r-- | src/test/thread/.gitignore | 1 | ||||
-rw-r--r-- | src/test/thread/Makefile | 24 | ||||
-rw-r--r-- | src/test/thread/README | 54 | ||||
-rw-r--r-- | src/test/thread/thread_test.c | 459 |
4 files changed, 538 insertions, 0 deletions
diff --git a/src/test/thread/.gitignore b/src/test/thread/.gitignore new file mode 100644 index 0000000..1d54d54 --- /dev/null +++ b/src/test/thread/.gitignore @@ -0,0 +1 @@ +/thread_test diff --git a/src/test/thread/Makefile b/src/test/thread/Makefile new file mode 100644 index 0000000..a13c0c6 --- /dev/null +++ b/src/test/thread/Makefile @@ -0,0 +1,24 @@ +#------------------------------------------------------------------------- +# +# Makefile for tools/thread +# +# Copyright (c) 2003-2020, PostgreSQL Global Development Group +# +# src/test/thread/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/tools/thread +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +override CFLAGS += $(PTHREAD_CFLAGS) + +all: thread_test + +thread_test: thread_test.o +# no need for $LIBS, might not be compiled yet + $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(PTHREAD_LIBS) -o $@$(X) + +clean distclean maintainer-clean: + rm -f thread_test$(X) thread_test.o diff --git a/src/test/thread/README b/src/test/thread/README new file mode 100644 index 0000000..4da2344 --- /dev/null +++ b/src/test/thread/README @@ -0,0 +1,54 @@ +src/test/thread/README + +Threading +========= + +This program is run by configure to determine if threading is +properly supported on the platform. + +You can run the program manually to see details, which shows if your +native libc functions are thread-safe, or if we use *_r functions or +thread locking. + +To use this program manually, you must: + + o run "configure" + o compile the main source tree + o compile and run this program + +If your platform requires special thread flags that are not tested by +/config/acx_pthread.m4, add PTHREAD_CFLAGS and PTHREAD_LIBS defines to +your template/${port} file. + +Windows Systems +=============== + +Windows systems do not vary in their thread-safeness in the same way that +other systems might, nor do they generally have pthreads installed, hence +on Windows this test is skipped by the configure program (pthreads is +required by the test program, but not PostgreSQL itself). If you do wish +to test your system however, you can do so as follows: + +1) Install pthreads in you Mingw/Msys environment. You can download pthreads + from ftp://sources.redhat.com/pub/pthreads-win32/. + +2) Build the test program: + + gcc -o thread_test.exe \ + -D_REENTRANT \ + -D_THREAD_SAFE \ + -D_POSIX_PTHREAD_SEMANTICS \ + -I../../../src/include/port/win32 \ + thread_test.c \ + -lws2_32 \ + -lpthreadgc2 + +3) Run thread_test.exe. You should see output like: + + dpage@PC30:/cvs/pgsql/src/tools/thread$ ./thread_test + Your GetLastError() is thread-safe. + Your system uses strerror() which is thread-safe. + getpwuid_r()/getpwuid() are not applicable to Win32 platforms. + Your system uses gethostbyname which is thread-safe. + + Your platform is thread-safe. diff --git a/src/test/thread/thread_test.c b/src/test/thread/thread_test.c new file mode 100644 index 0000000..e1bec01 --- /dev/null +++ b/src/test/thread/thread_test.c @@ -0,0 +1,459 @@ +/*------------------------------------------------------------------------- + * + * thread_test.c + * libc thread test program + * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/test/thread/thread_test.c + * + * This program tests to see if your standard libc functions use + * pthread_setspecific()/pthread_getspecific() to be thread-safe. + * See src/port/thread.c for more details. + * + * This program first tests to see if each function returns a constant + * memory pointer within the same thread, then, assuming it does, tests + * to see if the pointers are different for different threads. If they + * are, the function is thread-safe. + * + *------------------------------------------------------------------------- + */ + +#if !defined(IN_CONFIGURE) && !defined(WIN32) +#include "postgres.h" + +/* we want to know what the native strerror does, not pg_strerror */ +#undef strerror +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <netdb.h> +#include <sys/types.h> +#include <pwd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +/* CYGWIN requires this for MAXHOSTNAMELEN */ +#ifdef __CYGWIN__ +#include <sys/param.h> +#endif + +#ifdef WIN32 +#define MAXHOSTNAMELEN 63 +#include <winsock2.h> +#endif + + +/* Test for POSIX.1c 2-arg sigwait() and fail on single-arg version */ +#include <signal.h> +int sigwait(const sigset_t *set, int *sig); + + +#if !defined(ENABLE_THREAD_SAFETY) && !defined(IN_CONFIGURE) && !defined(WIN32) +int +main(int argc, char *argv[]) +{ + fprintf(stderr, "This PostgreSQL build does not support threads.\n"); + fprintf(stderr, "Perhaps rerun 'configure' using '--enable-thread-safety'.\n"); + return 1; +} +#else + +/* This must be down here because this is the code that uses threads. */ +#include <pthread.h> + +#define TEMP_FILENAME_1 "thread_test.1" +#define TEMP_FILENAME_2 "thread_test.2" + +static void func_call_1(void); +static void func_call_2(void); + +static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; + +static volatile int thread1_done = 0; +static volatile int thread2_done = 0; + +static volatile int errno1_set = 0; +static volatile int errno2_set = 0; + +#ifndef HAVE_STRERROR_R +static char *strerror_p1; +static char *strerror_p2; +static int strerror_threadsafe = 0; +#endif + +#if !defined(WIN32) && !defined(HAVE_GETPWUID_R) +static struct passwd *passwd_p1; +static struct passwd *passwd_p2; +static int getpwuid_threadsafe = 0; +#endif + +#if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R) +static struct hostent *hostent_p1; +static struct hostent *hostent_p2; +static char myhostname[MAXHOSTNAMELEN]; +static int gethostbyname_threadsafe = 0; +#endif + +static int platform_is_threadsafe = 1; + +int +main(int argc, char *argv[]) +{ + pthread_t thread1, + thread2; + int rc; + +#ifdef WIN32 + WSADATA wsaData; + int err; +#endif + + if (argc > 1) + { + fprintf(stderr, "Usage: %s\n", argv[0]); + return 1; + } + +#ifdef IN_CONFIGURE + /* Send stdout to 'config.log' */ + close(1); + dup(5); +#endif + +#ifdef WIN32 + err = WSAStartup(MAKEWORD(1, 1), &wsaData); + if (err != 0) + { + fprintf(stderr, "Cannot start the network subsystem - %d**\nexiting\n", err); + exit(1); + } +#endif + +#if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R) + if (gethostname(myhostname, MAXHOSTNAMELEN) != 0) + { + fprintf(stderr, "Cannot get local hostname **\nexiting\n"); + exit(1); + } +#endif + + /* Hold lock until we are ready for the child threads to exit. */ + pthread_mutex_lock(&init_mutex); + + rc = pthread_create(&thread1, NULL, (void *(*) (void *)) func_call_1, NULL); + if (rc != 0) + { + fprintf(stderr, "Failed to create thread 1: %s **\nexiting\n", + strerror(errno)); + exit(1); + } + rc = pthread_create(&thread2, NULL, (void *(*) (void *)) func_call_2, NULL); + if (rc != 0) + { + /* + * strerror() might not be thread-safe, and we already spawned thread + * 1 that uses it, so avoid using it. + */ + fprintf(stderr, "Failed to create thread 2 **\nexiting\n"); + exit(1); + } + + while (thread1_done == 0 || thread2_done == 0) + sched_yield(); /* if this is a portability problem, remove it */ + + /* Test things while we have thread-local storage */ + + /* If we got here, we didn't exit() from a thread */ +#ifdef WIN32 + printf("Your GetLastError() is thread-safe.\n"); +#else + printf("Your errno is thread-safe.\n"); +#endif + +#ifndef HAVE_STRERROR_R + if (strerror_p1 != strerror_p2) + strerror_threadsafe = 1; +#endif + +#if !defined(WIN32) && !defined(HAVE_GETPWUID_R) + if (passwd_p1 != passwd_p2) + getpwuid_threadsafe = 1; +#endif + +#if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R) + if (hostent_p1 != hostent_p2) + gethostbyname_threadsafe = 1; +#endif + + /* close down threads */ + + pthread_mutex_unlock(&init_mutex); /* let children exit */ + + pthread_join(thread1, NULL); /* clean up children */ + pthread_join(thread2, NULL); + + /* report results */ + +#ifdef HAVE_STRERROR_R + printf("Your system has strerror_r(); it does not need strerror().\n"); +#else + printf("Your system uses strerror() which is "); + if (strerror_threadsafe) + printf("thread-safe.\n"); + else + { + printf("not thread-safe. **\n"); + platform_is_threadsafe = 0; + } +#endif + +#ifdef WIN32 + printf("getpwuid_r()/getpwuid() are not applicable to Win32 platforms.\n"); +#elif defined(HAVE_GETPWUID_R) + printf("Your system has getpwuid_r(); it does not need getpwuid().\n"); +#else + printf("Your system uses getpwuid() which is "); + if (getpwuid_threadsafe) + printf("thread-safe.\n"); + else + { + printf("not thread-safe. **\n"); + platform_is_threadsafe = 0; + } +#endif + +#ifdef HAVE_GETADDRINFO + printf("Your system has getaddrinfo(); it does not need gethostbyname()\n" + " or gethostbyname_r().\n"); +#elif defined(HAVE_GETHOSTBYNAME_R) + printf("Your system has gethostbyname_r(); it does not need gethostbyname().\n"); +#else + printf("Your system uses gethostbyname which is "); + if (gethostbyname_threadsafe) + printf("thread-safe.\n"); + else + { + printf("not thread-safe. **\n"); + platform_is_threadsafe = 0; + } +#endif + + if (platform_is_threadsafe) + { + printf("\nYour platform is thread-safe.\n"); + return 0; + } + else + { + printf("\n** YOUR PLATFORM IS NOT THREAD-SAFE. **\n"); + return 1; + } +} + +/* + * func_call_1 + */ +static void +func_call_1(void) +{ +#if !defined(HAVE_GETPWUID_R) || \ + (!defined(HAVE_GETADDRINFO) && \ + !defined(HAVE_GETHOSTBYNAME_R)) + void *p; +#endif +#ifdef WIN32 + HANDLE h1; +#else + int fd; +#endif + + unlink(TEMP_FILENAME_1); + + /* Set errno = EEXIST */ + + /* create, then try to fail on exclusive create open */ + + /* + * It would be great to check errno here but if errno is not thread-safe + * we might get a value from the other thread and mis-report the cause of + * the failure. + */ +#ifdef WIN32 + if ((h1 = CreateFile(TEMP_FILENAME_1, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL)) == + INVALID_HANDLE_VALUE) +#else + if ((fd = open(TEMP_FILENAME_1, O_RDWR | O_CREAT, 0600)) < 0) +#endif + { + fprintf(stderr, "Could not create file %s in current directory\n", + TEMP_FILENAME_1); + exit(1); + } + +#ifdef WIN32 + if (CreateFile(TEMP_FILENAME_1, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL) + != INVALID_HANDLE_VALUE) +#else + if (open(TEMP_FILENAME_1, O_RDWR | O_CREAT | O_EXCL, 0600) >= 0) +#endif + { + fprintf(stderr, + "Could not generate failure for exclusive file create of %s in current directory **\nexiting\n", + TEMP_FILENAME_1); + exit(1); + } + + /* + * Wait for other thread to set errno. We can't use thread-specific + * locking here because it might affect errno. + */ + errno1_set = 1; + while (errno2_set == 0) + sched_yield(); + +#ifdef WIN32 + if (GetLastError() != ERROR_FILE_EXISTS) +#else + if (errno != EEXIST) +#endif + { +#ifdef WIN32 + fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n"); +#else + fprintf(stderr, "errno not thread-safe **\nexiting\n"); +#endif + unlink(TEMP_FILENAME_1); + exit(1); + } + +#ifdef WIN32 + CloseHandle(h1); +#else + close(fd); +#endif + unlink(TEMP_FILENAME_1); + +#ifndef HAVE_STRERROR_R + + /* + * If strerror() uses sys_errlist, the pointer might change for different + * errno values, so we don't check to see if it varies within the thread. + */ + strerror_p1 = strerror(EACCES); +#endif + +#if !defined(WIN32) && !defined(HAVE_GETPWUID_R) + passwd_p1 = getpwuid(0); + p = getpwuid(1); + if (passwd_p1 != p) + { + printf("Your getpwuid() changes the static memory area between calls\n"); + passwd_p1 = NULL; /* force thread-safe failure report */ + } +#endif + +#if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R) + /* threads do this in opposite order */ + hostent_p1 = gethostbyname(myhostname); + p = gethostbyname("localhost"); + if (hostent_p1 != p) + { + printf("Your gethostbyname() changes the static memory area between calls\n"); + hostent_p1 = NULL; /* force thread-safe failure report */ + } +#endif + + thread1_done = 1; + pthread_mutex_lock(&init_mutex); /* wait for parent to test */ + pthread_mutex_unlock(&init_mutex); +} + + +/* + * func_call_2 + */ +static void +func_call_2(void) +{ +#if !defined(HAVE_GETPWUID_R) || \ + (!defined(HAVE_GETADDRINFO) && \ + !defined(HAVE_GETHOSTBYNAME_R)) + void *p; +#endif + + unlink(TEMP_FILENAME_2); + + /* Set errno = ENOENT */ + + /* This will fail, but we can't check errno yet */ + if (unlink(TEMP_FILENAME_2) != -1) + { + fprintf(stderr, + "Could not generate failure for unlink of %s in current directory **\nexiting\n", + TEMP_FILENAME_2); + exit(1); + } + + /* + * Wait for other thread to set errno. We can't use thread-specific + * locking here because it might affect errno. + */ + errno2_set = 1; + while (errno1_set == 0) + sched_yield(); + +#ifdef WIN32 + if (GetLastError() != ENOENT) +#else + if (errno != ENOENT) +#endif + { +#ifdef WIN32 + fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n"); +#else + fprintf(stderr, "errno not thread-safe **\nexiting\n"); +#endif + exit(1); + } + +#ifndef HAVE_STRERROR_R + + /* + * If strerror() uses sys_errlist, the pointer might change for different + * errno values, so we don't check to see if it varies within the thread. + */ + strerror_p2 = strerror(EINVAL); +#endif + +#if !defined(WIN32) && !defined(HAVE_GETPWUID_R) + passwd_p2 = getpwuid(2); + p = getpwuid(3); + if (passwd_p2 != p) + { + printf("Your getpwuid() changes the static memory area between calls\n"); + passwd_p2 = NULL; /* force thread-safe failure report */ + } +#endif + +#if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R) + /* threads do this in opposite order */ + hostent_p2 = gethostbyname("localhost"); + p = gethostbyname(myhostname); + if (hostent_p2 != p) + { + printf("Your gethostbyname() changes the static memory area between calls\n"); + hostent_p2 = NULL; /* force thread-safe failure report */ + } +#endif + + thread2_done = 1; + pthread_mutex_lock(&init_mutex); /* wait for parent to test */ + pthread_mutex_unlock(&init_mutex); +} + +#endif /* !ENABLE_THREAD_SAFETY && !IN_CONFIGURE */ |