PostgreSQL Source Code  git master
thread_test.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * thread_test.c
4  * libc threading test program
5  *
6  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * config/thread_test.c
10  *
11  * This program tests to see if your standard libc functions use
12  * pthread_setspecific()/pthread_getspecific() to be thread-safe.
13  * See src/port/thread.c for more details.
14  *
15  * This program first tests to see if each function returns a constant
16  * memory pointer within the same thread, then, assuming it does, tests
17  * to see if the pointers are different for different threads. If they
18  * are, the function is thread-safe.
19  *
20  *-------------------------------------------------------------------------
21  */
22 
23 /* We cannot use c.h, as port.h will not exist yet */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <netdb.h>
29 #include <sys/types.h>
30 #include <pwd.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <pthread.h>
35 
36 /* CYGWIN requires this for MAXHOSTNAMELEN */
37 #ifdef __CYGWIN__
38 #include <sys/param.h>
39 #endif
40 
41 #ifdef WIN32
42 #define MAXHOSTNAMELEN 63
43 #include <winsock2.h>
44 #endif
45 
46 /* Test for POSIX.1c 2-arg sigwait() and fail on single-arg version */
47 #include <signal.h>
48 int sigwait(const sigset_t *set, int *sig);
49 
50 
51 #define TEMP_FILENAME_1 "thread_test.1"
52 #define TEMP_FILENAME_2 "thread_test.2"
53 
54 static void func_call_1(void);
55 static void func_call_2(void);
56 
57 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
58 
59 static volatile int thread1_done = 0;
60 static volatile int thread2_done = 0;
61 
62 static volatile int errno1_set = 0;
63 static volatile int errno2_set = 0;
64 
65 #ifndef HAVE_STRERROR_R
66 static char *strerror_p1;
67 static char *strerror_p2;
68 static int strerror_threadsafe = 0;
69 #endif
70 
71 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
72 static struct passwd *passwd_p1;
73 static struct passwd *passwd_p2;
74 static int getpwuid_threadsafe = 0;
75 #endif
76 
77 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
78 static struct hostent *hostent_p1;
79 static struct hostent *hostent_p2;
80 static char myhostname[MAXHOSTNAMELEN];
81 static int gethostbyname_threadsafe = 0;
82 #endif
83 
84 static int platform_is_threadsafe = 1;
85 
86 int
87 main(int argc, char *argv[])
88 {
89  pthread_t thread1,
90  thread2;
91  int rc;
92 
93 #ifdef WIN32
94  WSADATA wsaData;
95  int err;
96 #endif
97 
98  if (argc > 1)
99  {
100  fprintf(stderr, "Usage: %s\n", argv[0]);
101  return 1;
102  }
103 
104  /* Send stdout to 'config.log' */
105  close(1);
106  dup(5);
107 
108 #ifdef WIN32
109  err = WSAStartup(MAKEWORD(2, 2), &wsaData);
110  if (err != 0)
111  {
112  fprintf(stderr, "Cannot start the network subsystem - %d**\nexiting\n", err);
113  exit(1);
114  }
115 #endif
116 
117 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
118  if (gethostname(myhostname, MAXHOSTNAMELEN) != 0)
119  {
120  fprintf(stderr, "Cannot get local hostname **\nexiting\n");
121  exit(1);
122  }
123 #endif
124 
125  /* Hold lock until we are ready for the child threads to exit. */
127 
128  rc = pthread_create(&thread1, NULL, (void *(*) (void *)) func_call_1, NULL);
129  if (rc != 0)
130  {
131  fprintf(stderr, "Failed to create thread 1: %s **\nexiting\n",
132  strerror(errno));
133  exit(1);
134  }
135  rc = pthread_create(&thread2, NULL, (void *(*) (void *)) func_call_2, NULL);
136  if (rc != 0)
137  {
138  /*
139  * strerror() might not be thread-safe, and we already spawned thread
140  * 1 that uses it, so avoid using it.
141  */
142  fprintf(stderr, "Failed to create thread 2 **\nexiting\n");
143  exit(1);
144  }
145 
146  while (thread1_done == 0 || thread2_done == 0)
147  sched_yield(); /* if this is a portability problem, remove it */
148 
149  /* Test things while we have thread-local storage */
150 
151  /* If we got here, we didn't exit() from a thread */
152 #ifdef WIN32
153  printf("Your GetLastError() is thread-safe.\n");
154 #else
155  printf("Your errno is thread-safe.\n");
156 #endif
157 
158 #ifndef HAVE_STRERROR_R
159  if (strerror_p1 != strerror_p2)
161 #endif
162 
163 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
164  if (passwd_p1 != passwd_p2)
166 #endif
167 
168 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
169  if (hostent_p1 != hostent_p2)
171 #endif
172 
173  /* close down threads */
174 
175  pthread_mutex_unlock(&init_mutex); /* let children exit */
176 
177  pthread_join(thread1, NULL); /* clean up children */
178  pthread_join(thread2, NULL);
179 
180  /* report results */
181 
182 #ifdef HAVE_STRERROR_R
183  printf("Your system has strerror_r(); it does not need strerror().\n");
184 #else
185  printf("Your system uses strerror() which is ");
187  printf("thread-safe.\n");
188  else
189  {
190  printf("not thread-safe. **\n");
192  }
193 #endif
194 
195 #ifdef WIN32
196  printf("getpwuid_r()/getpwuid() are not applicable to Win32 platforms.\n");
197 #elif defined(HAVE_GETPWUID_R)
198  printf("Your system has getpwuid_r(); it does not need getpwuid().\n");
199 #else
200  printf("Your system uses getpwuid() which is ");
202  printf("thread-safe.\n");
203  else
204  {
205  printf("not thread-safe. **\n");
207  }
208 #endif
209 
210 #ifdef HAVE_GETADDRINFO
211  printf("Your system has getaddrinfo(); it does not need gethostbyname()\n"
212  " or gethostbyname_r().\n");
213 #elif defined(HAVE_GETHOSTBYNAME_R)
214  printf("Your system has gethostbyname_r(); it does not need gethostbyname().\n");
215 #else
216  printf("Your system uses gethostbyname which is ");
218  printf("thread-safe.\n");
219  else
220  {
221  printf("not thread-safe. **\n");
223  }
224 #endif
225 
227  {
228  printf("\nYour platform is thread-safe.\n");
229  return 0;
230  }
231  else
232  {
233  printf("\n** YOUR PLATFORM IS NOT THREAD-SAFE. **\n");
234  return 1;
235  }
236 }
237 
238 /*
239  * func_call_1
240  */
241 static void
243 {
244 #if !defined(HAVE_GETPWUID_R) || \
245  (!defined(HAVE_GETADDRINFO) && \
246  !defined(HAVE_GETHOSTBYNAME_R))
247  void *p;
248 #endif
249 #ifdef WIN32
250  HANDLE h1;
251 #else
252  int fd;
253 #endif
254 
255  unlink(TEMP_FILENAME_1);
256 
257  /* Set errno = EEXIST */
258 
259  /* create, then try to fail on exclusive create open */
260 
261  /*
262  * It would be great to check errno here but if errno is not thread-safe
263  * we might get a value from the other thread and mis-report the cause of
264  * the failure.
265  */
266 #ifdef WIN32
267  if ((h1 = CreateFile(TEMP_FILENAME_1, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL)) ==
268  INVALID_HANDLE_VALUE)
269 #else
270  if ((fd = open(TEMP_FILENAME_1, O_RDWR | O_CREAT, 0600)) < 0)
271 #endif
272  {
273  fprintf(stderr, "Could not create file %s in current directory\n",
275  exit(1);
276  }
277 
278 #ifdef WIN32
279  if (CreateFile(TEMP_FILENAME_1, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL)
280  != INVALID_HANDLE_VALUE)
281 #else
282  if (open(TEMP_FILENAME_1, O_RDWR | O_CREAT | O_EXCL, 0600) >= 0)
283 #endif
284  {
285  fprintf(stderr,
286  "Could not generate failure for exclusive file create of %s in current directory **\nexiting\n",
288  exit(1);
289  }
290 
291  /*
292  * Wait for other thread to set errno. We can't use thread-specific
293  * locking here because it might affect errno.
294  */
295  errno1_set = 1;
296  while (errno2_set == 0)
297  sched_yield();
298 
299 #ifdef WIN32
300  if (GetLastError() != ERROR_FILE_EXISTS)
301 #else
302  if (errno != EEXIST)
303 #endif
304  {
305 #ifdef WIN32
306  fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n");
307 #else
308  fprintf(stderr, "errno not thread-safe **\nexiting\n");
309 #endif
310  unlink(TEMP_FILENAME_1);
311  exit(1);
312  }
313 
314 #ifdef WIN32
315  CloseHandle(h1);
316 #else
317  close(fd);
318 #endif
319  unlink(TEMP_FILENAME_1);
320 
321 #ifndef HAVE_STRERROR_R
322 
323  /*
324  * If strerror() uses sys_errlist, the pointer might change for different
325  * errno values, so we don't check to see if it varies within the thread.
326  */
327  strerror_p1 = strerror(EACCES);
328 #endif
329 
330 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
331  passwd_p1 = getpwuid(0);
332  p = getpwuid(1);
333  if (passwd_p1 != p)
334  {
335  printf("Your getpwuid() changes the static memory area between calls\n");
336  passwd_p1 = NULL; /* force thread-safe failure report */
337  }
338 #endif
339 
340 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
341  /* threads do this in opposite order */
342  hostent_p1 = gethostbyname(myhostname);
343  p = gethostbyname("localhost");
344  if (hostent_p1 != p)
345  {
346  printf("Your gethostbyname() changes the static memory area between calls\n");
347  hostent_p1 = NULL; /* force thread-safe failure report */
348  }
349 #endif
350 
351  thread1_done = 1;
352  pthread_mutex_lock(&init_mutex); /* wait for parent to test */
354 }
355 
356 
357 /*
358  * func_call_2
359  */
360 static void
362 {
363 #if !defined(HAVE_GETPWUID_R) || \
364  (!defined(HAVE_GETADDRINFO) && \
365  !defined(HAVE_GETHOSTBYNAME_R))
366  void *p;
367 #endif
368 
369  unlink(TEMP_FILENAME_2);
370 
371  /* Set errno = ENOENT */
372 
373  /* This will fail, but we can't check errno yet */
374  if (unlink(TEMP_FILENAME_2) != -1)
375  {
376  fprintf(stderr,
377  "Could not generate failure for unlink of %s in current directory **\nexiting\n",
379  exit(1);
380  }
381 
382  /*
383  * Wait for other thread to set errno. We can't use thread-specific
384  * locking here because it might affect errno.
385  */
386  errno2_set = 1;
387  while (errno1_set == 0)
388  sched_yield();
389 
390 #ifdef WIN32
391  if (GetLastError() != ENOENT)
392 #else
393  if (errno != ENOENT)
394 #endif
395  {
396 #ifdef WIN32
397  fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n");
398 #else
399  fprintf(stderr, "errno not thread-safe **\nexiting\n");
400 #endif
401  exit(1);
402  }
403 
404 #ifndef HAVE_STRERROR_R
405 
406  /*
407  * If strerror() uses sys_errlist, the pointer might change for different
408  * errno values, so we don't check to see if it varies within the thread.
409  */
410  strerror_p2 = strerror(EINVAL);
411 #endif
412 
413 #if !defined(WIN32) && !defined(HAVE_GETPWUID_R)
414  passwd_p2 = getpwuid(2);
415  p = getpwuid(3);
416  if (passwd_p2 != p)
417  {
418  printf("Your getpwuid() changes the static memory area between calls\n");
419  passwd_p2 = NULL; /* force thread-safe failure report */
420  }
421 #endif
422 
423 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
424  /* threads do this in opposite order */
425  hostent_p2 = gethostbyname("localhost");
426  p = gethostbyname(myhostname);
427  if (hostent_p2 != p)
428  {
429  printf("Your gethostbyname() changes the static memory area between calls\n");
430  hostent_p2 = NULL; /* force thread-safe failure report */
431  }
432 #endif
433 
434  thread2_done = 1;
435  pthread_mutex_lock(&init_mutex); /* wait for parent to test */
437 }
#define TEMP_FILENAME_2
Definition: thread_test.c:52
static volatile int errno1_set
Definition: thread_test.c:62
CRITICAL_SECTION * pthread_mutex_t
Definition: pthread-win32.h:8
static struct passwd * passwd_p2
Definition: thread_test.c:73
static char * strerror_p2
Definition: thread_test.c:67
static int platform_is_threadsafe
Definition: thread_test.c:84
static volatile int thread2_done
Definition: thread_test.c:60
int sigwait(const sigset_t *set, int *sig)
#define printf(...)
Definition: port.h:221
static volatile int errno2_set
Definition: thread_test.c:63
#define fprintf
Definition: port.h:219
static int fd(const char *x, int i)
Definition: preproc-init.c:105
static struct hostent * hostent_p2
Definition: thread_test.c:79
static pthread_mutex_t init_mutex
Definition: thread_test.c:57
static struct hostent * hostent_p1
Definition: thread_test.c:78
int pthread_mutex_lock(pthread_mutex_t *mp)
Definition: pthread-win32.c:45
static void func_call_2(void)
Definition: thread_test.c:361
static int strerror_threadsafe
Definition: thread_test.c:68
int pthread_mutex_unlock(pthread_mutex_t *mp)
Definition: pthread-win32.c:54
static struct passwd * passwd_p1
Definition: thread_test.c:72
static int sig
Definition: pg_ctl.c:84
static void func_call_1(void)
Definition: thread_test.c:242
#define strerror
Definition: port.h:228
static volatile int thread1_done
Definition: thread_test.c:59
#define TEMP_FILENAME_1
Definition: thread_test.c:51
int main(int argc, char *argv[])
Definition: thread_test.c:87
#define pthread_t
Definition: pgbench.c:128
static char * strerror_p1
Definition: thread_test.c:66
#define close(a)
Definition: win32.h:12
static int gethostbyname_threadsafe
Definition: thread_test.c:81
static int getpwuid_threadsafe
Definition: thread_test.c:74
static char myhostname[MAXHOSTNAMELEN]
Definition: thread_test.c:80