]> granicus.if.org Git - postgresql/blob - src/test/thread/thread_test.c
Update copyright for 2014
[postgresql] / src / test / thread / thread_test.c
1 /*-------------------------------------------------------------------------
2  *
3  * test_thread_funcs.c
4  *              libc thread test program
5  *
6  * Portions Copyright (c) 1996-2014, 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
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
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
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
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 */