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