]> granicus.if.org Git - postgresql/blob - src/tools/thread/thread_test.c
Do thread testing from configure in a much cleaner fashion.
[postgresql] / src / tools / thread / thread_test.c
1 /*-------------------------------------------------------------------------
2  *
3  * test_thread_funcs.c
4  *              libc thread test program
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *      $PostgreSQL: pgsql/src/tools/thread/thread_test.c,v 1.29 2004/04/27 19:51:12 momjian Exp $
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 #ifndef IN_CONFIGURE
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 #if !defined(ENABLE_THREAD_SAFETY) && !defined(IN_CONFIGURE)
51 int
52 main(int argc, char *argv[])
53 {
54         fprintf(stderr, "This PostgreSQL build does not support threads.\n");
55         fprintf(stderr, "Perhaps rerun 'configure' using '--enable-thread-safety'.\n");
56         return 1;
57 }
58
59 #else
60
61 /* This must be down here because this is the code that uses threads. */
62 #include "pthread.h"
63
64 void            func_call_1(void);
65 void            func_call_2(void);
66
67 #define         TEMP_FILENAME_1 "/tmp/thread_test.1.XXXXX"
68 #define         TEMP_FILENAME_2 "/tmp/thread_test.2.XXXXX"
69
70 char       *temp_filename_1;
71 char       *temp_filename_2;
72
73 pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
74
75 volatile int thread1_done = 0;
76 volatile int thread2_done = 0;
77
78 volatile int errno1_set = 0;
79 volatile int errno2_set = 0;
80
81 #ifndef HAVE_STRERROR_R
82 char       *strerror_p1;
83 char       *strerror_p2;
84 bool            strerror_threadsafe = false;
85 #endif
86
87 #ifndef HAVE_GETPWUID_R
88 struct passwd *passwd_p1;
89 struct passwd *passwd_p2;
90 bool            getpwuid_threadsafe = false;
91 #endif
92
93 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
94 struct hostent *hostent_p1;
95 struct hostent *hostent_p2;
96 char            myhostname[MAXHOSTNAMELEN];
97 bool            gethostbyname_threadsafe = false;
98 #endif
99
100 bool            platform_is_threadsafe = true;
101
102 int
103 main(int argc, char *argv[])
104 {
105         pthread_t       thread1,
106                                 thread2;
107
108         if (argc > 1)
109         {
110                 fprintf(stderr, "Usage: %s\n", argv[0]);
111                 return 1;
112         }
113
114 #ifdef IN_CONFIGURE
115         /* Send stdout to 'config.log' */
116         close(1);
117         dup(5);
118 #endif
119         
120         /* Make temp filenames, might not have strdup() */
121         temp_filename_1 = malloc(strlen(TEMP_FILENAME_1) + 1);
122         strcpy(temp_filename_1, TEMP_FILENAME_1);
123         mktemp(temp_filename_1);
124
125         temp_filename_2 = malloc(strlen(TEMP_FILENAME_2) + 1);
126         strcpy(temp_filename_2, TEMP_FILENAME_2);
127         mktemp(temp_filename_2);
128         
129 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
130         if (gethostname(myhostname, MAXHOSTNAMELEN) != 0)
131         {
132                 fprintf(stderr, "Can not get local hostname **\nexiting\n");
133                 exit(1);
134         }
135 #endif
136
137         /* Hold lock until we are ready for the child threads to exit. */
138         pthread_mutex_lock(&init_mutex);
139
140         pthread_create(&thread1, NULL, (void *(*) (void *)) func_call_1, NULL);
141         pthread_create(&thread2, NULL, (void *(*) (void *)) func_call_2, NULL);
142
143         while (thread1_done == 0 || thread2_done == 0)
144                 sched_yield();                  /* if this is a portability problem,
145                                                                  * remove it */
146
147         printf("Your errno is thread-safe.\n");
148
149 #ifndef HAVE_STRERROR_R
150         if (strerror_p1 != strerror_p2)
151                 strerror_threadsafe = true;
152 #endif
153
154 #ifndef HAVE_GETPWUID_R
155         if (passwd_p1 != passwd_p2)
156                 getpwuid_threadsafe = true;
157 #endif
158
159 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
160         if (hostent_p1 != hostent_p2)
161                 gethostbyname_threadsafe = true;
162 #endif
163
164         pthread_mutex_unlock(&init_mutex);      /* let children exit  */
165
166         pthread_join(thread1, NULL);    /* clean up children */
167         pthread_join(thread2, NULL);
168
169 #ifdef HAVE_STRERROR_R
170         printf("Your system has sterror_r();  it does not need strerror().\n");
171 #else
172         printf("Your system uses strerror() which is ");
173         if (strerror_threadsafe)
174                 printf("thread-safe.\n");
175         else
176         {
177                 printf("not thread-safe. **\n");
178                 platform_is_threadsafe = false;
179         }
180 #endif
181
182 #ifdef HAVE_GETPWUID_R
183         printf("Your system has getpwuid_r();  it does not need getpwuid().\n");
184 #else
185         printf("Your system uses getpwuid() which is ");
186         if (getpwuid_threadsafe)
187                 printf("thread-safe.\n");
188         else
189         {
190                 printf("not thread-safe. **\n");
191                 platform_is_threadsafe = false;
192         }
193 #endif
194
195 #ifdef HAVE_GETADDRINFO
196         printf("Your system has getaddrinfo();  it does not need gethostbyname()\n"
197                    "  or gethostbyname_r().\n");
198 #else
199 #ifdef HAVE_GETHOSTBYNAME_R
200         printf("Your system has gethostbyname_r();  it does not need gethostbyname().\n");
201 #else
202         printf("Your system uses gethostbyname which is ");
203         if (gethostbyname_threadsafe)
204                 printf("thread-safe.\n");
205         else
206         {
207                 printf("not thread-safe. **\n");
208                 platform_is_threadsafe = false;
209         }
210 #endif
211 #endif
212
213         if (platform_is_threadsafe)
214         {
215                 printf("\nYour platform is thread-safe.\n");
216                 return 0;
217         }
218         else
219         {
220                 printf("\n** YOUR PLATFORM IS NOT THREAD-SAFE. **\n");
221                 return 1;
222         }
223 }
224
225 void
226 func_call_1(void)
227 {
228 #if !defined(HAVE_GETPWUID_R) || \
229         (!defined(HAVE_GETADDRINFO) && \
230          !defined(HAVE_GETHOSTBYNAME_R))
231         void       *p;
232 #endif
233
234         unlink(temp_filename_1);
235         /* create, then try to fail on exclusive create open */
236         if (open(temp_filename_1, O_RDWR | O_CREAT, 0600) < 0 ||
237                 open(temp_filename_1, O_RDWR | O_CREAT | O_EXCL, 0600) >= 0)
238         {
239                 fprintf(stderr, "Could not create file in /tmp or\n");
240                 fprintf(stderr, "Could not generate failure for create file in /tmp **\nexiting\n");
241                 exit(1);
242         }
243
244         /*
245          * Wait for other thread to set errno. We can't use thread-specific
246          * locking here because it might affect errno.
247          */
248         errno1_set = 1;
249         while (errno2_set == 0)
250                 sched_yield();
251         if (errno != EEXIST)
252         {
253                 fprintf(stderr, "errno not thread-safe **\nexiting\n");
254                 unlink(temp_filename_1);
255                 exit(1);
256         }
257         unlink(temp_filename_1);
258
259 #ifndef HAVE_STRERROR_R
260         strerror_p1 = strerror(EACCES);
261
262         /*
263          * If strerror() uses sys_errlist, the pointer might change for
264          * different errno values, so we don't check to see if it varies
265          * within the thread.
266          */
267 #endif
268
269 #ifndef HAVE_GETPWUID_R
270         passwd_p1 = getpwuid(0);
271         p = getpwuid(1);
272         if (passwd_p1 != p)
273         {
274                 printf("Your getpwuid() changes the static memory area between calls\n");
275                 passwd_p1 = NULL;               /* force thread-safe failure report */
276         }
277 #endif
278
279 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
280         /* threads do this in opposite order */
281         hostent_p1 = gethostbyname(myhostname);
282         p = gethostbyname("localhost");
283         if (hostent_p1 != p)
284         {
285                 printf("Your gethostbyname() changes the static memory area between calls\n");
286                 hostent_p1 = NULL;              /* force thread-safe failure report */
287         }
288 #endif
289
290         thread1_done = 1;
291         pthread_mutex_lock(&init_mutex);        /* wait for parent to test */
292         pthread_mutex_unlock(&init_mutex);
293 }
294
295
296 void
297 func_call_2(void)
298 {
299 #if !defined(HAVE_GETPWUID_R) || \
300         (!defined(HAVE_GETADDRINFO) && \
301          !defined(HAVE_GETHOSTBYNAME_R))
302         void       *p;
303 #endif
304
305         unlink(temp_filename_2);
306         /* open non-existant file */
307         if (open(temp_filename_2, O_RDONLY, 0600) >= 0)
308         {
309                 fprintf(stderr, "Read-only open succeeded without create **\nexiting\n");
310                 exit(1);
311         }
312
313         /*
314          * Wait for other thread to set errno. We can't use thread-specific
315          * locking here because it might affect errno.
316          */
317         errno2_set = 1;
318         while (errno1_set == 0)
319                 sched_yield();
320         if (errno != ENOENT)
321         {
322                 fprintf(stderr, "errno not thread-safe **\nexiting\n");
323                 unlink(temp_filename_2);
324                 exit(1);
325         }
326         unlink(temp_filename_2);
327
328 #ifndef HAVE_STRERROR_R
329         strerror_p2 = strerror(EINVAL);
330
331         /*
332          * If strerror() uses sys_errlist, the pointer might change for
333          * different errno values, so we don't check to see if it varies
334          * within the thread.
335          */
336 #endif
337
338 #ifndef HAVE_GETPWUID_R
339         passwd_p2 = getpwuid(2);
340         p = getpwuid(3);
341         if (passwd_p2 != p)
342         {
343                 printf("Your getpwuid() changes the static memory area between calls\n");
344                 passwd_p2 = NULL;               /* force thread-safe failure report */
345         }
346 #endif
347
348 #if !defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME_R)
349         /* threads do this in opposite order */
350         hostent_p2 = gethostbyname("localhost");
351         p = gethostbyname(myhostname);
352         if (hostent_p2 != p)
353         {
354                 printf("Your gethostbyname() changes the static memory area between calls\n");
355                 hostent_p2 = NULL;              /* force thread-safe failure report */
356         }
357 #endif
358
359         thread2_done = 1;
360         pthread_mutex_lock(&init_mutex);        /* wait for parent to test */
361         pthread_mutex_unlock(&init_mutex);
362 }
363 #endif /* !ENABLE_THREAD_SAFETY && !IN_CONFIGURE */