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