postgresql/src/test/thread/thread_test.c

460 lines
11 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* 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
*
2010-09-21 04:08:53 +08:00
* 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>
2005-10-15 10:49:52 +08:00
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"
2005-10-15 10:49:52 +08:00
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
2005-10-15 10:49:52 +08:00
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;
2005-10-15 10:49:52 +08:00
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;
2005-10-15 10:49:52 +08:00
int err;
#endif
2004-08-29 13:07:03 +08:00
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
2004-08-29 13:07:03 +08:00
#ifdef WIN32
err = WSAStartup(MAKEWORD(2, 2), &wsaData);
2005-10-15 10:49:52 +08:00
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)
2005-10-15 10:49:52 +08:00
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
2004-04-22 04:58:56 +08:00
printf("Your system uses strerror() which is ");
if (strerror_threadsafe)
2004-04-23 07:58:03 +08:00
printf("thread-safe.\n");
2004-04-22 04:58:56 +08:00
else
{
2004-04-26 08:36:18 +08:00
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
2004-04-22 04:58:56 +08:00
printf("Your system uses getpwuid() which is ");
if (getpwuid_threadsafe)
2004-04-23 07:58:03 +08:00
printf("thread-safe.\n");
2004-04-22 04:58:56 +08:00
else
{
2004-04-26 08:36:18 +08:00
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
2004-04-22 04:58:56 +08:00
printf("Your system uses gethostbyname which is ");
if (gethostbyname_threadsafe)
2004-04-23 07:58:03 +08:00
printf("thread-safe.\n");
2004-04-22 04:58:56 +08:00
else
{
2004-04-26 08:36:18 +08:00
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;
2005-10-15 10:49:52 +08:00
#endif
unlink(TEMP_FILENAME_1);
2005-10-15 10:49:52 +08:00
/* Set errno = EEXIST */
2004-03-28 10:37:31 +08:00
/* 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
2005-10-15 10:49:52 +08:00
if (GetLastError() != ERROR_FILE_EXISTS)
#else
2005-10-15 10:49:52 +08:00
if (errno != EEXIST)
#endif
{
#ifdef WIN32
2005-10-15 10:49:52 +08:00
fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n");
#else
2005-10-15 10:49:52 +08:00
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
/*
2005-10-15 10:49:52 +08:00
* 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
2005-10-15 10:49:52 +08:00
if (GetLastError() != ENOENT)
#else
2005-10-15 10:49:52 +08:00
if (errno != ENOENT)
#endif
{
#ifdef WIN32
2005-10-15 10:49:52 +08:00
fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n");
#else
2005-10-15 10:49:52 +08:00
fprintf(stderr, "errno not thread-safe **\nexiting\n");
#endif
exit(1);
}
#ifndef HAVE_STRERROR_R
/*
2005-10-15 10:49:52 +08:00
* 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);
}
2004-08-29 13:07:03 +08:00
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-22 03:18:54 +08:00
#endif /* !ENABLE_THREAD_SAFETY && !IN_CONFIGURE */