Fix obsolete comment regarding FSM truncation.
[PostgreSQL.git] / src / test / thread / thread_test.c
blob2fe305f8ac4a253600137a16753915b54298d2f9
1 /*-------------------------------------------------------------------------
3 * test_thread_funcs.c
4 * libc thread test program
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * $PostgreSQL$
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.
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.
20 *-------------------------------------------------------------------------
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
31 #ifndef true
32 #define true ((bool) 1)
33 #endif
35 #ifndef false
36 #define false ((bool) 0)
37 #endif
38 #endif
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>
50 /* CYGWIN requires this for MAXHOSTNAMELEN */
51 #ifdef __CYGWIN__
52 #include <sys/param.h>
53 #endif
55 /******************************************************************
56 * Windows Hacks
57 *****************************************************************/
59 #ifdef WIN32
60 #define MAXHOSTNAMELEN 63
61 #include <winsock2.h>
63 int mkstemp(char *template);
65 int
66 mkstemp(char *template)
68 FILE *foo;
70 mktemp(template);
71 foo = fopen(template, "rw");
72 if (!foo)
73 return -1;
74 else
75 return (int) foo;
77 #endif
79 /******************************************************************
80 * End Windows Hacks
81 *****************************************************************/
84 /* Test for POSIX.1c 2-arg sigwait() and fail on single-arg version */
85 #include <signal.h>
86 int sigwait(const sigset_t *set, int *sig);
89 #if !defined(ENABLE_THREAD_SAFETY) && !defined(IN_CONFIGURE) && !(defined(WIN32))
90 int
91 main(int argc, char *argv[])
93 fprintf(stderr, "This PostgreSQL build does not support threads.\n");
94 fprintf(stderr, "Perhaps rerun 'configure' using '--enable-thread-safety'.\n");
95 return 1;
97 #else
99 /* This must be down here because this is the code that uses threads. */
100 #include <pthread.h>
102 static void func_call_1(void);
103 static void func_call_2(void);
105 #ifdef WIN32
106 #define TEMP_FILENAME_1 "thread_test.1.XXXXXX"
107 #define TEMP_FILENAME_2 "thread_test.2.XXXXXX"
108 #else
109 #define TEMP_FILENAME_1 "/tmp/thread_test.1.XXXXXX"
110 #define TEMP_FILENAME_2 "/tmp/thread_test.2.XXXXXX"
111 #endif
113 static char *temp_filename_1;
114 static char *temp_filename_2;
116 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
118 static volatile int thread1_done = 0;
119 static volatile int thread2_done = 0;
121 static volatile int errno1_set = 0;
122 static volatile int errno2_set = 0;
124 #ifndef HAVE_STRERROR_R
125 static char *strerror_p1;
126 static char *strerror_p2;
127 static bool strerror_threadsafe = false;
128 #endif
130 #ifndef WIN32
131 #ifndef HAVE_GETPWUID_R
132 static struct passwd *passwd_p1;
133 static struct passwd *passwd_p2;
134 static bool getpwuid_threadsafe = false;
135 #endif
136 #endif
138 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
139 static struct hostent *hostent_p1;
140 static struct hostent *hostent_p2;
141 static char myhostname[MAXHOSTNAMELEN];
142 static bool gethostbyname_threadsafe = false;
143 #endif
145 static bool platform_is_threadsafe = true;
148 main(int argc, char *argv[])
150 pthread_t thread1,
151 thread2;
152 int fd;
154 #ifdef WIN32
155 WSADATA wsaData;
156 int err;
157 #endif
159 if (argc > 1)
161 fprintf(stderr, "Usage: %s\n", argv[0]);
162 return 1;
165 #ifdef IN_CONFIGURE
166 /* Send stdout to 'config.log' */
167 close(1);
168 dup(5);
169 #endif
171 #ifdef WIN32
172 err = WSAStartup(MAKEWORD(1, 1), &wsaData);
173 if (err != 0)
175 fprintf(stderr, "Cannot start the network subsystem - %d**\nexiting\n", err);
176 exit(1);
178 #endif
180 /* Make temp filenames, might not have strdup() */
181 temp_filename_1 = malloc(strlen(TEMP_FILENAME_1) + 1);
182 strcpy(temp_filename_1, TEMP_FILENAME_1);
183 fd = mkstemp(temp_filename_1);
184 close(fd);
186 temp_filename_2 = malloc(strlen(TEMP_FILENAME_2) + 1);
187 strcpy(temp_filename_2, TEMP_FILENAME_2);
188 fd = mkstemp(temp_filename_2);
189 close(fd);
191 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
192 if (gethostname(myhostname, MAXHOSTNAMELEN) != 0)
194 fprintf(stderr, "Cannot get local hostname **\nexiting\n");
195 exit(1);
197 #endif
199 /* Hold lock until we are ready for the child threads to exit. */
200 pthread_mutex_lock(&init_mutex);
202 pthread_create(&thread1, NULL, (void *(*) (void *)) func_call_1, NULL);
203 pthread_create(&thread2, NULL, (void *(*) (void *)) func_call_2, NULL);
205 while (thread1_done == 0 || thread2_done == 0)
206 sched_yield(); /* if this is a portability problem, remove it */
207 #ifdef WIN32
208 printf("Your GetLastError() is thread-safe.\n");
209 #else
210 printf("Your errno is thread-safe.\n");
211 #endif
213 #ifndef HAVE_STRERROR_R
214 if (strerror_p1 != strerror_p2)
215 strerror_threadsafe = true;
216 #endif
218 #ifndef WIN32
219 #ifndef HAVE_GETPWUID_R
220 if (passwd_p1 != passwd_p2)
221 getpwuid_threadsafe = true;
222 #endif
223 #endif
225 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
226 if (hostent_p1 != hostent_p2)
227 gethostbyname_threadsafe = true;
228 #endif
230 pthread_mutex_unlock(&init_mutex); /* let children exit */
232 pthread_join(thread1, NULL); /* clean up children */
233 pthread_join(thread2, NULL);
235 #ifdef HAVE_STRERROR_R
236 printf("Your system has sterror_r(); it does not need strerror().\n");
237 #else
238 printf("Your system uses strerror() which is ");
239 if (strerror_threadsafe)
240 printf("thread-safe.\n");
241 else
243 printf("not thread-safe. **\n");
244 platform_is_threadsafe = false;
246 #endif
248 #ifndef WIN32
249 #ifdef HAVE_GETPWUID_R
250 printf("Your system has getpwuid_r(); it does not need getpwuid().\n");
251 #else
252 printf("Your system uses getpwuid() which is ");
253 if (getpwuid_threadsafe)
254 printf("thread-safe.\n");
255 else
257 printf("not thread-safe. **\n");
258 platform_is_threadsafe = false;
260 #endif
261 #else
262 printf("getpwuid_r()/getpwuid() are not applicable to Win32 platforms.\n");
263 #endif
265 #ifdef HAVE_GETADDRINFO
266 printf("Your system has getaddrinfo(); it does not need gethostbyname()\n"
267 " or gethostbyname_r().\n");
268 #else
269 #ifdef HAVE_GETHOSTBYNAME_R
270 printf("Your system has gethostbyname_r(); it does not need gethostbyname().\n");
271 #else
272 printf("Your system uses gethostbyname which is ");
273 if (gethostbyname_threadsafe)
274 printf("thread-safe.\n");
275 else
277 printf("not thread-safe. **\n");
278 platform_is_threadsafe = false;
280 #endif
281 #endif
283 if (platform_is_threadsafe)
285 printf("\nYour platform is thread-safe.\n");
286 return 0;
288 else
290 printf("\n** YOUR PLATFORM IS NOT THREAD-SAFE. **\n");
291 return 1;
295 static void
296 func_call_1(void)
298 #if !defined(HAVE_GETPWUID_R) || \
299 (!defined(HAVE_GETADDRINFO) && \
300 !defined(HAVE_GETHOSTBYNAME_R))
301 void *p;
302 #endif
303 #ifdef WIN32
304 HANDLE h1;
305 HANDLE h2;
306 #endif
307 unlink(temp_filename_1);
310 /* create, then try to fail on exclusive create open */
311 #ifdef WIN32
312 h1 = CreateFile(temp_filename_1, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
313 h2 = CreateFile(temp_filename_1, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
314 if (h1 == INVALID_HANDLE_VALUE || GetLastError() != ERROR_FILE_EXISTS)
315 #else
316 if (open(temp_filename_1, O_RDWR | O_CREAT, 0600) < 0 ||
317 open(temp_filename_1, O_RDWR | O_CREAT | O_EXCL, 0600) >= 0)
318 #endif
320 #ifdef WIN32
321 fprintf(stderr, "Could not create file in current directory or\n");
322 fprintf(stderr, "Could not generate failure for create file in current directory **\nexiting\n");
323 #else
324 fprintf(stderr, "Could not create file in /tmp or\n");
325 fprintf(stderr, "Could not generate failure for create file in /tmp **\nexiting\n");
326 #endif
327 exit(1);
331 * Wait for other thread to set errno. We can't use thread-specific
332 * locking here because it might affect errno.
334 errno1_set = 1;
335 while (errno2_set == 0)
336 sched_yield();
337 #ifdef WIN32
338 if (GetLastError() != ERROR_FILE_EXISTS)
339 #else
340 if (errno != EEXIST)
341 #endif
343 #ifdef WIN32
344 fprintf(stderr, "GetLastError() not thread-safe **\nexiting\n");
345 #else
346 fprintf(stderr, "errno not thread-safe **\nexiting\n");
347 #endif
348 unlink(temp_filename_1);
349 exit(1);
351 unlink(temp_filename_1);
353 #ifndef HAVE_STRERROR_R
354 strerror_p1 = strerror(EACCES);
357 * If strerror() uses sys_errlist, the pointer might change for different
358 * errno values, so we don't check to see if it varies within the thread.
360 #endif
362 #ifndef WIN32
363 #ifndef HAVE_GETPWUID_R
364 passwd_p1 = getpwuid(0);
365 p = getpwuid(1);
366 if (passwd_p1 != p)
368 printf("Your getpwuid() changes the static memory area between calls\n");
369 passwd_p1 = NULL; /* force thread-safe failure report */
371 #endif
372 #endif
374 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
375 /* threads do this in opposite order */
376 hostent_p1 = gethostbyname(myhostname);
377 p = gethostbyname("localhost");
378 if (hostent_p1 != p)
380 printf("Your gethostbyname() changes the static memory area between calls\n");
381 hostent_p1 = NULL; /* force thread-safe failure report */
383 #endif
385 thread1_done = 1;
386 pthread_mutex_lock(&init_mutex); /* wait for parent to test */
387 pthread_mutex_unlock(&init_mutex);
391 static void
392 func_call_2(void)
394 #if !defined(HAVE_GETPWUID_R) || \
395 (!defined(HAVE_GETADDRINFO) && \
396 !defined(HAVE_GETHOSTBYNAME_R))
397 void *p;
398 #endif
400 unlink(temp_filename_2);
401 /* open non-existant file */
402 #ifdef WIN32
403 CreateFile(temp_filename_2, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
404 if (GetLastError() != ERROR_FILE_NOT_FOUND)
405 #else
406 if (open(temp_filename_2, O_RDONLY, 0600) >= 0)
407 #endif
409 fprintf(stderr, "Read-only open succeeded without create **\nexiting\n");
410 exit(1);
414 * Wait for other thread to set errno. We can't use thread-specific
415 * locking here because it might affect errno.
417 errno2_set = 1;
418 while (errno1_set == 0)
419 sched_yield();
420 #ifdef WIN32
421 if (GetLastError() != ENOENT)
422 #else
423 if (errno != ENOENT)
424 #endif
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 unlink(temp_filename_2);
432 exit(1);
434 unlink(temp_filename_2);
436 #ifndef HAVE_STRERROR_R
437 strerror_p2 = strerror(EINVAL);
440 * If strerror() uses sys_errlist, the pointer might change for different
441 * errno values, so we don't check to see if it varies within the thread.
443 #endif
445 #ifndef WIN32
446 #ifndef HAVE_GETPWUID_R
447 passwd_p2 = getpwuid(2);
448 p = getpwuid(3);
449 if (passwd_p2 != p)
451 printf("Your getpwuid() changes the static memory area between calls\n");
452 passwd_p2 = NULL; /* force thread-safe failure report */
454 #endif
455 #endif
457 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
458 /* threads do this in opposite order */
459 hostent_p2 = gethostbyname("localhost");
460 p = gethostbyname(myhostname);
461 if (hostent_p2 != p)
463 printf("Your gethostbyname() changes the static memory area between calls\n");
464 hostent_p2 = NULL; /* force thread-safe failure report */
466 #endif
468 thread2_done = 1;
469 pthread_mutex_lock(&init_mutex); /* wait for parent to test */
470 pthread_mutex_unlock(&init_mutex);
473 #endif /* !ENABLE_THREAD_SAFETY && !IN_CONFIGURE */