pthreadpool: Add test for pthread_create failure
[samba.git] / lib / pthreadpool / tests.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <poll.h>
4 #include <errno.h>
5 #include <stdlib.h>
6 #include <pthread.h>
7 #include <unistd.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <signal.h>
11 #include "pthreadpool_pipe.h"
12 #include "pthreadpool_tevent.h"
13
14 static int test_init(void)
15 {
16         struct pthreadpool_pipe *p;
17         int ret;
18
19         ret = pthreadpool_pipe_init(1, &p);
20         if (ret != 0) {
21                 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
22                         strerror(ret));
23                 return -1;
24         }
25         ret = pthreadpool_pipe_destroy(p);
26         if (ret != 0) {
27                 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
28                         strerror(ret));
29                 return -1;
30         }
31         return 0;
32 }
33
34 static void test_sleep(void *ptr)
35 {
36         int *ptimeout = (int *)ptr;
37         int ret;
38         ret = poll(NULL, 0, *ptimeout);
39         if (ret != 0) {
40                 fprintf(stderr, "poll returned %d (%s)\n",
41                         ret, strerror(errno));
42         }
43 }
44
45 static int test_jobs(int num_threads, int num_jobs)
46 {
47         char *finished;
48         struct pthreadpool_pipe *p;
49         int timeout = 1;
50         int i, ret;
51
52         finished = (char *)calloc(1, num_jobs);
53         if (finished == NULL) {
54                 fprintf(stderr, "calloc failed\n");
55                 return -1;
56         }
57
58         ret = pthreadpool_pipe_init(num_threads, &p);
59         if (ret != 0) {
60                 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
61                         strerror(ret));
62                 return -1;
63         }
64
65         for (i=0; i<num_jobs; i++) {
66                 ret = pthreadpool_pipe_add_job(p, i, test_sleep, &timeout);
67                 if (ret != 0) {
68                         fprintf(stderr, "pthreadpool_pipe_add_job failed: "
69                                 "%s\n", strerror(ret));
70                         return -1;
71                 }
72         }
73
74         for (i=0; i<num_jobs; i++) {
75                 int jobid = -1;
76                 ret = pthreadpool_pipe_finished_jobs(p, &jobid, 1);
77                 if (ret < 0) {
78                         fprintf(stderr, "pthreadpool_pipe_finished_jobs "
79                                 "failed: %s\n", strerror(-ret));
80                         return -1;
81                 }
82                 if ((ret != 1) || (jobid >= num_jobs)) {
83                         fprintf(stderr, "invalid job number %d\n", jobid);
84                         return -1;
85                 }
86                 finished[jobid] += 1;
87         }
88
89         for (i=0; i<num_jobs; i++) {
90                 if (finished[i] != 1) {
91                         fprintf(stderr, "finished[%d] = %d\n",
92                                 i, finished[i]);
93                         return -1;
94                 }
95         }
96
97         ret = pthreadpool_pipe_destroy(p);
98         if (ret != 0) {
99                 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
100                         strerror(ret));
101                 return -1;
102         }
103
104         free(finished);
105         return 0;
106 }
107
108 static int test_busydestroy(void)
109 {
110         struct pthreadpool_pipe *p;
111         int timeout = 50;
112         struct pollfd pfd;
113         int ret, jobid;
114
115         ret = pthreadpool_pipe_init(1, &p);
116         if (ret != 0) {
117                 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
118                         strerror(ret));
119                 return -1;
120         }
121         ret = pthreadpool_pipe_add_job(p, 1, test_sleep, &timeout);
122         if (ret != 0) {
123                 fprintf(stderr, "pthreadpool_pipe_add_job failed: %s\n",
124                         strerror(ret));
125                 return -1;
126         }
127         ret = pthreadpool_pipe_destroy(p);
128         if (ret != EBUSY) {
129                 fprintf(stderr, "Could destroy a busy pool\n");
130                 return -1;
131         }
132
133         pfd.fd = pthreadpool_pipe_signal_fd(p);
134         pfd.events = POLLIN|POLLERR;
135
136         do {
137                 ret = poll(&pfd, 1, -1);
138         } while ((ret == -1) && (errno == EINTR));
139
140         ret = pthreadpool_pipe_finished_jobs(p, &jobid, 1);
141         if (ret < 0) {
142                 fprintf(stderr, "pthreadpool_pipe_finished_jobs failed: %s\n",
143                         strerror(-ret));
144                 return -1;
145         }
146
147         ret = pthreadpool_pipe_destroy(p);
148         if (ret != 0) {
149                 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
150                         strerror(ret));
151                 return -1;
152         }
153         return 0;
154 }
155
156 static int test_fork(void)
157 {
158         struct pthreadpool_pipe *p;
159         pid_t child, waited;
160         int status, ret;
161
162         ret = pthreadpool_pipe_init(1, &p);
163         if (ret != 0) {
164                 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
165                         strerror(ret));
166                 return -1;
167         }
168         ret = pthreadpool_pipe_destroy(p);
169         if (ret != 0) {
170                 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
171                         strerror(ret));
172                 return -1;
173         }
174
175         child = fork();
176         if (child < 0) {
177                 perror("fork failed");
178                 return -1;
179         }
180         if (child == 0) {
181                 exit(0);
182         }
183         waited = wait(&status);
184         if (waited == -1) {
185                 perror("wait failed");
186                 return -1;
187         }
188         if (waited != child) {
189                 fprintf(stderr, "expected child %d, got %d\n",
190                         (int)child, (int)waited);
191                 return -1;
192         }
193         return 0;
194 }
195
196 static void busyfork_job(void *private_data)
197 {
198         return;
199 }
200
201 static int test_busyfork(void)
202 {
203         struct pthreadpool_pipe *p;
204         int fds[2];
205         struct pollfd pfd;
206         pid_t child, waitret;
207         int ret, jobnum, wstatus;
208
209         ret = pipe(fds);
210         if (ret == -1) {
211                 perror("pipe failed");
212                 return -1;
213         }
214
215         ret = pthreadpool_pipe_init(1, &p);
216         if (ret != 0) {
217                 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
218                         strerror(ret));
219                 return -1;
220         }
221
222         ret = pthreadpool_pipe_add_job(p, 1, busyfork_job, NULL);
223         if (ret != 0) {
224                 fprintf(stderr, "pthreadpool_add_job failed: %s\n",
225                         strerror(ret));
226                 return -1;
227         }
228
229         ret = pthreadpool_pipe_finished_jobs(p, &jobnum, 1);
230         if (ret != 1) {
231                 fprintf(stderr, "pthreadpool_pipe_finished_jobs failed\n");
232                 return -1;
233         }
234
235         ret = poll(NULL, 0, 200);
236         if (ret == -1) {
237                 perror("poll failed");
238                 return -1;
239         }
240
241         child = fork();
242         if (child < 0) {
243                 perror("fork failed");
244                 return -1;
245         }
246
247         if (child == 0) {
248                 ret = pthreadpool_pipe_destroy(p);
249                 if (ret != 0) {
250                         fprintf(stderr, "pthreadpool_pipe_destroy failed: "
251                                 "%s\n", strerror(ret));
252                         exit(1);
253                 }
254                 exit(0);
255         }
256
257         ret = close(fds[1]);
258         if (ret == -1) {
259                 perror("close failed");
260                 return -1;
261         }
262
263         pfd = (struct pollfd) { .fd = fds[0], .events = POLLIN };
264
265         ret = poll(&pfd, 1, 5000);
266         if (ret == -1) {
267                 perror("poll failed");
268                 return -1;
269         }
270         if (ret == 0) {
271                 fprintf(stderr, "Child did not exit for 5 seconds\n");
272                 /*
273                  * The child might hang forever in
274                  * pthread_cond_destroy for example. Be kind to the
275                  * system and kill it.
276                  */
277                 kill(child, SIGTERM);
278                 return -1;
279         }
280         if (ret != 1) {
281                 fprintf(stderr, "poll returned %d -- huh??\n", ret);
282                 return -1;
283         }
284
285         ret = poll(NULL, 0, 200);
286         if (ret == -1) {
287                 perror("poll failed");
288                 return -1;
289         }
290
291         waitret = waitpid(child, &wstatus, WNOHANG);
292         if (waitret != child) {
293                 fprintf(stderr, "waitpid returned %d\n", (int)waitret);
294                 return -1;
295         }
296
297         if (!WIFEXITED(wstatus)) {
298                 fprintf(stderr, "child did not properly exit\n");
299                 return -1;
300         }
301
302         ret = WEXITSTATUS(wstatus);
303         if (ret != 0) {
304                 fprintf(stderr, "child returned %d\n", ret);
305                 return -1;
306         }
307
308         return 0;
309 }
310
311 static void test_tevent_wait(void *private_data)
312 {
313         int *timeout = private_data;
314         poll(NULL, 0, *timeout);
315 }
316
317 static int test_tevent_1(void)
318 {
319         struct tevent_context *ev;
320         struct pthreadpool_tevent *pool;
321         struct tevent_req *req1, *req2;
322         int timeout10 = 10;
323         int timeout100 = 100;
324         int ret;
325         bool ok;
326
327         ev = tevent_context_init(NULL);
328         if (ev == NULL) {
329                 ret = errno;
330                 fprintf(stderr, "tevent_context_init failed: %s\n",
331                         strerror(ret));
332                 return ret;
333         }
334         ret = pthreadpool_tevent_init(ev, 0, &pool);
335         if (ret != 0) {
336                 fprintf(stderr, "pthreadpool_tevent_init failed: %s\n",
337                         strerror(ret));
338                 TALLOC_FREE(ev);
339                 return ret;
340         }
341         req1 = pthreadpool_tevent_job_send(
342                 ev, ev, pool, test_tevent_wait, &timeout10);
343         if (req1 == NULL) {
344                 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
345                 TALLOC_FREE(ev);
346                 return ENOMEM;
347         }
348         req2 = pthreadpool_tevent_job_send(
349                 ev, ev, pool, test_tevent_wait, &timeout100);
350         if (req2 == NULL) {
351                 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
352                 TALLOC_FREE(ev);
353                 return ENOMEM;
354         }
355         ok = tevent_req_poll(req2, ev);
356         if (!ok) {
357                 ret = errno;
358                 fprintf(stderr, "tevent_req_poll failed: %s\n",
359                         strerror(ret));
360                 TALLOC_FREE(ev);
361                 return ret;
362         }
363         ret = pthreadpool_tevent_job_recv(req1);
364         TALLOC_FREE(req1);
365         if (ret != 0) {
366                 fprintf(stderr, "tevent_req_poll failed: %s\n",
367                         strerror(ret));
368                 TALLOC_FREE(ev);
369                 return ret;
370         }
371
372         TALLOC_FREE(req2);
373
374         ret = tevent_loop_wait(ev);
375         if (ret != 0) {
376                 fprintf(stderr, "tevent_loop_wait failed\n");
377                 return ret;
378         }
379
380         TALLOC_FREE(pool);
381         TALLOC_FREE(ev);
382         return 0;
383 }
384
385 int main(void)
386 {
387         int ret;
388
389         ret = test_tevent_1();
390         if (ret != 0) {
391                 fprintf(stderr, "test_event_1 failed: %s\n",
392                         strerror(ret));
393                 return 1;
394         }
395
396         ret = test_init();
397         if (ret != 0) {
398                 fprintf(stderr, "test_init failed\n");
399                 return 1;
400         }
401
402         ret = test_fork();
403         if (ret != 0) {
404                 fprintf(stderr, "test_fork failed\n");
405                 return 1;
406         }
407
408         ret = test_jobs(10, 10000);
409         if (ret != 0) {
410                 fprintf(stderr, "test_jobs failed\n");
411                 return 1;
412         }
413
414         ret = test_busydestroy();
415         if (ret != 0) {
416                 fprintf(stderr, "test_busydestroy failed\n");
417                 return 1;
418         }
419
420         ret = test_busyfork();
421         if (ret != 0) {
422                 fprintf(stderr, "test_busyfork failed\n");
423                 return 1;
424         }
425
426         printf("success\n");
427         return 0;
428 }