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