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