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