Header And Logo

PostgreSQL
| The world's most advanced open source database.

thread_test.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * test_thread_funcs.c
00004  *      libc thread test program
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *  src/test/thread/thread_test.c
00010  *
00011  *  This program tests to see if your standard libc functions use
00012  *  pthread_setspecific()/pthread_getspecific() to be thread-safe.
00013  *  See src/port/thread.c for more details.
00014  *
00015  *  This program first tests to see if each function returns a constant
00016  *  memory pointer within the same thread, then, assuming it does, tests
00017  *  to see if the pointers are different for different threads.  If they
00018  *  are, the function is thread-safe.
00019  *
00020  *-------------------------------------------------------------------------
00021  */
00022 
00023 #if !defined(IN_CONFIGURE) && !defined(WIN32)
00024 #include "postgres.h"
00025 #else
00026 /* From src/include/c.h" */
00027 #ifndef bool
00028 typedef char bool;
00029 #endif
00030 
00031 #ifndef true
00032 #define true    ((bool) 1)
00033 #endif
00034 
00035 #ifndef false
00036 #define false   ((bool) 0)
00037 #endif
00038 #endif
00039 
00040 #include <stdio.h>
00041 #include <stdlib.h>
00042 #include <unistd.h>
00043 #include <netdb.h>
00044 #include <sys/types.h>
00045 #include <pwd.h>
00046 #include <string.h>
00047 #include <fcntl.h>
00048 #include <errno.h>
00049 
00050 /* CYGWIN requires this for MAXHOSTNAMELEN */
00051 #ifdef __CYGWIN__
00052 #include <sys/param.h>
00053 #endif
00054 
00055 #ifdef WIN32
00056 #define MAXHOSTNAMELEN 63
00057 #include <winsock2.h>
00058 #endif
00059 
00060 
00061 /* Test for POSIX.1c 2-arg sigwait() and fail on single-arg version */
00062 #include <signal.h>
00063 int         sigwait(const sigset_t *set, int *sig);
00064 
00065 
00066 #if !defined(ENABLE_THREAD_SAFETY) && !defined(IN_CONFIGURE) && !defined(WIN32)
00067 int
00068 main(int argc, char *argv[])
00069 {
00070     fprintf(stderr, "This PostgreSQL build does not support threads.\n");
00071     fprintf(stderr, "Perhaps rerun 'configure' using '--enable-thread-safety'.\n");
00072     return 1;
00073 }
00074 #else
00075 
00076 /* This must be down here because this is the code that uses threads. */
00077 #include <pthread.h>
00078 
00079 #define     TEMP_FILENAME_1 "thread_test.1"
00080 #define     TEMP_FILENAME_2 "thread_test.2"
00081 
00082 static void func_call_1(void);
00083 static void func_call_2(void);
00084 
00085 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
00086 
00087 static volatile int thread1_done = 0;
00088 static volatile int thread2_done = 0;
00089 
00090 static volatile int errno1_set = 0;
00091 static volatile int errno2_set = 0;
00092 
00093 #ifndef HAVE_STRERROR_R
00094 static char *strerror_p1;
00095 static char *strerror_p2;
00096 static bool strerror_threadsafe = false;
00097 #endif
00098 
00099 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
00100 static struct passwd *passwd_p1;
00101 static struct passwd *passwd_p2;
00102 static bool getpwuid_threadsafe = false;
00103 #endif
00104 
00105 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
00106 static struct hostent *hostent_p1;
00107 static struct hostent *hostent_p2;
00108 static char myhostname[MAXHOSTNAMELEN];
00109 static bool gethostbyname_threadsafe = false;
00110 #endif
00111 
00112 static bool platform_is_threadsafe = true;
00113 
00114 int
00115 main(int argc, char *argv[])
00116 {
00117     pthread_t   thread1,
00118                 thread2;
00119     int         rc;
00120 
00121 #ifdef WIN32
00122     WSADATA     wsaData;
00123     int         err;
00124 #endif
00125 
00126     if (argc > 1)
00127     {
00128         fprintf(stderr, "Usage: %s\n", argv[0]);
00129         return 1;
00130     }
00131 
00132 #ifdef IN_CONFIGURE
00133     /* Send stdout to 'config.log' */
00134     close(1);
00135     dup(5);
00136 #endif
00137 
00138 #ifdef WIN32
00139     err = WSAStartup(MAKEWORD(1, 1), &wsaData);
00140     if (err != 0)
00141     {
00142         fprintf(stderr, "Cannot start the network subsystem - %d**\nexiting\n", err);
00143         exit(1);
00144     }
00145 #endif
00146 
00147 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
00148     if (gethostname(myhostname, MAXHOSTNAMELEN) != 0)
00149     {
00150         fprintf(stderr, "Cannot get local hostname **\nexiting\n");
00151         exit(1);
00152     }
00153 #endif
00154 
00155     /* Hold lock until we are ready for the child threads to exit. */
00156     pthread_mutex_lock(&init_mutex);
00157 
00158     rc = pthread_create(&thread1, NULL, (void *(*) (void *)) func_call_1, NULL);
00159     if (rc != 0)
00160     {
00161         fprintf(stderr, "Failed to create thread 1: %s **\nexiting\n",
00162                 strerror(errno));
00163         exit(1);
00164     }
00165     rc = pthread_create(&thread2, NULL, (void *(*) (void *)) func_call_2, NULL);
00166     if (rc != 0)
00167     {
00168         /*
00169          * strerror() might not be thread-safe, and we already spawned thread
00170          * 1 that uses it, so avoid using it.
00171          */
00172         fprintf(stderr, "Failed to create thread 2 **\nexiting\n");
00173         exit(1);
00174     }
00175 
00176     while (thread1_done == 0 || thread2_done == 0)
00177         sched_yield();          /* if this is a portability problem, remove it */
00178 
00179     /* Test things while we have thread-local storage */
00180 
00181     /* If we got here, we didn't exit() from a thread */
00182 #ifdef WIN32
00183     printf("Your GetLastError() is thread-safe.\n");
00184 #else
00185     printf("Your errno is thread-safe.\n");
00186 #endif
00187 
00188 #ifndef HAVE_STRERROR_R
00189     if (strerror_p1 != strerror_p2)
00190         strerror_threadsafe = true;
00191 #endif
00192 
00193 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
00194     if (passwd_p1 != passwd_p2)
00195         getpwuid_threadsafe = true;
00196 #endif
00197 
00198 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
00199     if (hostent_p1 != hostent_p2)
00200         gethostbyname_threadsafe = true;
00201 #endif
00202 
00203     /* close down threads */
00204 
00205     pthread_mutex_unlock(&init_mutex);  /* let children exit  */
00206 
00207     pthread_join(thread1, NULL);    /* clean up children */
00208     pthread_join(thread2, NULL);
00209 
00210     /* report results */
00211 
00212 #ifdef HAVE_STRERROR_R
00213     printf("Your system has sterror_r();  it does not need strerror().\n");
00214 #else
00215     printf("Your system uses strerror() which is ");
00216     if (strerror_threadsafe)
00217         printf("thread-safe.\n");
00218     else
00219     {
00220         printf("not thread-safe. **\n");
00221         platform_is_threadsafe = false;
00222     }
00223 #endif
00224 
00225 #ifdef WIN32
00226     printf("getpwuid_r()/getpwuid() are not applicable to Win32 platforms.\n");
00227 #elif defined(HAVE_GETPWUID_R)
00228     printf("Your system has getpwuid_r();  it does not need getpwuid().\n");
00229 #else
00230     printf("Your system uses getpwuid() which is ");
00231     if (getpwuid_threadsafe)
00232         printf("thread-safe.\n");
00233     else
00234     {
00235         printf("not thread-safe. **\n");
00236         platform_is_threadsafe = false;
00237     }
00238 #endif
00239 
00240 #ifdef HAVE_GETADDRINFO
00241     printf("Your system has getaddrinfo();  it does not need gethostbyname()\n"
00242            "  or gethostbyname_r().\n");
00243 #elif defined(HAVE_GETHOSTBYNAME_R)
00244     printf("Your system has gethostbyname_r();  it does not need gethostbyname().\n");
00245 #else
00246     printf("Your system uses gethostbyname which is ");
00247     if (gethostbyname_threadsafe)
00248         printf("thread-safe.\n");
00249     else
00250     {
00251         printf("not thread-safe. **\n");
00252         platform_is_threadsafe = false;
00253     }
00254 #endif
00255 
00256     if (platform_is_threadsafe)
00257     {
00258         printf("\nYour platform is thread-safe.\n");
00259         return 0;
00260     }
00261     else
00262     {
00263         printf("\n** YOUR PLATFORM IS NOT THREAD-SAFE. **\n");
00264         return 1;
00265     }
00266 }
00267 
00268 /*
00269  * func_call_1
00270  */
00271 static void
00272 func_call_1(void)
00273 {
00274 #if !defined(HAVE_GETPWUID_R) || \
00275     (!defined(HAVE_GETADDRINFO) && \
00276      !defined(HAVE_GETHOSTBYNAME_R))
00277     void       *p;
00278 #endif
00279 #ifdef WIN32
00280     HANDLE      h1;
00281 #else
00282     int         fd;
00283 #endif
00284 
00285     unlink(TEMP_FILENAME_1);
00286 
00287     /* Set errno = EEXIST */
00288 
00289     /* create, then try to fail on exclusive create open */
00290 
00291     /*
00292      * It would be great to check errno here but if errno is not thread-safe
00293      * we might get a value from the other thread and mis-report the cause of
00294      * the failure.
00295      */
00296 #ifdef WIN32
00297     if ((h1 = CreateFile(TEMP_FILENAME_1, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL)) ==
00298         INVALID_HANDLE_VALUE)
00299 #else
00300     if ((fd = open(TEMP_FILENAME_1, O_RDWR | O_CREAT, 0600)) < 0)
00301 #endif
00302     {
00303         fprintf(stderr, "Could not create file %s in current directory\n",
00304                 TEMP_FILENAME_1);
00305         exit(1);
00306     }
00307 
00308 #ifdef WIN32
00309     if (CreateFile(TEMP_FILENAME_1, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL)
00310         != INVALID_HANDLE_VALUE)
00311 #else
00312     if (open(TEMP_FILENAME_1, O_RDWR | O_CREAT | O_EXCL, 0600) >= 0)
00313 #endif
00314     {
00315         fprintf(stderr,
00316                 "Could not generate failure for exclusive file create of %s in current directory **\nexiting\n",
00317                 TEMP_FILENAME_1);
00318         exit(1);
00319     }
00320 
00321     /*
00322      * Wait for other thread to set errno. We can't use thread-specific
00323      * locking here because it might affect errno.
00324      */
00325     errno1_set = 1;
00326     while (errno2_set == 0)
00327         sched_yield();
00328 
00329 #ifdef WIN32
00330     if (GetLastError() != ERROR_FILE_EXISTS)
00331 #else
00332     if (errno != EEXIST)
00333 #endif
00334     {
00335 #ifdef WIN32
00336         fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n");
00337 #else
00338         fprintf(stderr, "errno not thread-safe **\nexiting\n");
00339 #endif
00340         unlink(TEMP_FILENAME_1);
00341         exit(1);
00342     }
00343 
00344 #ifdef WIN32
00345     CloseHandle(h1);
00346 #else
00347     close(fd);
00348 #endif
00349     unlink(TEMP_FILENAME_1);
00350 
00351 #ifndef HAVE_STRERROR_R
00352 
00353     /*
00354      * If strerror() uses sys_errlist, the pointer might change for different
00355      * errno values, so we don't check to see if it varies within the thread.
00356      */
00357     strerror_p1 = strerror(EACCES);
00358 #endif
00359 
00360 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
00361     passwd_p1 = getpwuid(0);
00362     p = getpwuid(1);
00363     if (passwd_p1 != p)
00364     {
00365         printf("Your getpwuid() changes the static memory area between calls\n");
00366         passwd_p1 = NULL;       /* force thread-safe failure report */
00367     }
00368 #endif
00369 
00370 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
00371     /* threads do this in opposite order */
00372     hostent_p1 = gethostbyname(myhostname);
00373     p = gethostbyname("localhost");
00374     if (hostent_p1 != p)
00375     {
00376         printf("Your gethostbyname() changes the static memory area between calls\n");
00377         hostent_p1 = NULL;      /* force thread-safe failure report */
00378     }
00379 #endif
00380 
00381     thread1_done = 1;
00382     pthread_mutex_lock(&init_mutex);    /* wait for parent to test */
00383     pthread_mutex_unlock(&init_mutex);
00384 }
00385 
00386 
00387 /*
00388  * func_call_2
00389  */
00390 static void
00391 func_call_2(void)
00392 {
00393 #if !defined(HAVE_GETPWUID_R) || \
00394     (!defined(HAVE_GETADDRINFO) && \
00395      !defined(HAVE_GETHOSTBYNAME_R))
00396     void       *p;
00397 #endif
00398 
00399     unlink(TEMP_FILENAME_2);
00400 
00401     /* Set errno = ENOENT */
00402 
00403     /* This will fail, but we can't check errno yet */
00404     if (unlink(TEMP_FILENAME_2) != -1)
00405     {
00406         fprintf(stderr,
00407                 "Could not generate failure for unlink of %s in current directory **\nexiting\n",
00408                 TEMP_FILENAME_2);
00409         exit(1);
00410     }
00411 
00412     /*
00413      * Wait for other thread to set errno. We can't use thread-specific
00414      * locking here because it might affect errno.
00415      */
00416     errno2_set = 1;
00417     while (errno1_set == 0)
00418         sched_yield();
00419 
00420 #ifdef WIN32
00421     if (GetLastError() != ENOENT)
00422 #else
00423     if (errno != ENOENT)
00424 #endif
00425     {
00426 #ifdef WIN32
00427         fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n");
00428 #else
00429         fprintf(stderr, "errno not thread-safe **\nexiting\n");
00430 #endif
00431         exit(1);
00432     }
00433 
00434 #ifndef HAVE_STRERROR_R
00435 
00436     /*
00437      * If strerror() uses sys_errlist, the pointer might change for different
00438      * errno values, so we don't check to see if it varies within the thread.
00439      */
00440     strerror_p2 = strerror(EINVAL);
00441 #endif
00442 
00443 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
00444     passwd_p2 = getpwuid(2);
00445     p = getpwuid(3);
00446     if (passwd_p2 != p)
00447     {
00448         printf("Your getpwuid() changes the static memory area between calls\n");
00449         passwd_p2 = NULL;       /* force thread-safe failure report */
00450     }
00451 #endif
00452 
00453 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
00454     /* threads do this in opposite order */
00455     hostent_p2 = gethostbyname("localhost");
00456     p = gethostbyname(myhostname);
00457     if (hostent_p2 != p)
00458     {
00459         printf("Your gethostbyname() changes the static memory area between calls\n");
00460         hostent_p2 = NULL;      /* force thread-safe failure report */
00461     }
00462 #endif
00463 
00464     thread2_done = 1;
00465     pthread_mutex_lock(&init_mutex);    /* wait for parent to test */
00466     pthread_mutex_unlock(&init_mutex);
00467 }
00468 
00469 #endif   /* !ENABLE_THREAD_SAFETY && !IN_CONFIGURE */