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
main(int argc,char * argv[])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
main(int argc,char * argv[])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
func_call_1(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
func_call_2(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 */
460