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