999118286eba7cbb45cfda5db8bc4aeae1f75b5e
[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         poll(NULL, 0, 200);
236
237         child = fork();
238         if (child < 0) {
239                 perror("fork failed");
240                 return -1;
241         }
242
243         if (child == 0) {
244                 ret = pthreadpool_pipe_destroy(p);
245                 if (ret != 0) {
246                         fprintf(stderr, "pthreadpool_pipe_destroy failed: "
247                                 "%s\n", strerror(ret));
248                         exit(1);
249                 }
250                 exit(0);
251         }
252
253         ret = close(fds[1]);
254         if (ret == -1) {
255                 perror("close failed");
256                 return -1;
257         }
258
259         pfd = (struct pollfd) { .fd = fds[0], .events = POLLIN };
260
261         ret = poll(&pfd, 1, 5000);
262         if (ret == -1) {
263                 perror("poll failed");
264                 return -1;
265         }
266         if (ret == 0) {
267                 fprintf(stderr, "Child did not exit for 5 seconds\n");
268                 /*
269                  * The child might hang forever in
270                  * pthread_cond_destroy for example. Be kind to the
271                  * system and kill it.
272                  */
273                 kill(child, SIGTERM);
274                 return -1;
275         }
276         if (ret != 1) {
277                 fprintf(stderr, "poll returned %d -- huh??\n", ret);
278                 return -1;
279         }
280
281         poll(NULL, 0, 200);
282
283         waitret = waitpid(child, &wstatus, WNOHANG);
284         if (waitret != child) {
285                 fprintf(stderr, "waitpid returned %d\n", (int)waitret);
286                 return -1;
287         }
288
289         if (!WIFEXITED(wstatus)) {
290                 fprintf(stderr, "child did not properly exit\n");
291                 return -1;
292         }
293
294         ret = WEXITSTATUS(wstatus);
295         if (ret != 0) {
296                 fprintf(stderr, "child returned %d\n", ret);
297                 return -1;
298         }
299
300         return 0;
301 }
302
303 static void test_tevent_wait(void *private_data)
304 {
305         int *timeout = private_data;
306         poll(NULL, 0, *timeout);
307 }
308
309 static int test_tevent_1(void)
310 {
311         struct tevent_context *ev;
312         struct pthreadpool_tevent *pool;
313         struct tevent_req *req1, *req2;
314         int timeout10 = 10;
315         int timeout100 = 100;
316         int ret;
317         bool ok;
318
319         ev = tevent_context_init(NULL);
320         if (ev == NULL) {
321                 ret = errno;
322                 fprintf(stderr, "tevent_context_init failed: %s\n",
323                         strerror(ret));
324                 return ret;
325         }
326         ret = pthreadpool_tevent_init(ev, 0, &pool);
327         if (ret != 0) {
328                 fprintf(stderr, "pthreadpool_tevent_init failed: %s\n",
329                         strerror(ret));
330                 TALLOC_FREE(ev);
331                 return ret;
332         }
333         req1 = pthreadpool_tevent_job_send(
334                 ev, ev, pool, test_tevent_wait, &timeout10);
335         if (req1 == NULL) {
336                 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
337                 TALLOC_FREE(ev);
338                 return ENOMEM;
339         }
340         req2 = pthreadpool_tevent_job_send(
341                 ev, ev, pool, test_tevent_wait, &timeout100);
342         if (req2 == NULL) {
343                 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
344                 TALLOC_FREE(ev);
345                 return ENOMEM;
346         }
347         ok = tevent_req_poll(req2, ev);
348         if (!ok) {
349                 ret = errno;
350                 fprintf(stderr, "tevent_req_poll failed: %s\n",
351                         strerror(ret));
352                 TALLOC_FREE(ev);
353                 return ret;
354         }
355         ret = pthreadpool_tevent_job_recv(req1);
356         TALLOC_FREE(req1);
357         if (ret != 0) {
358                 fprintf(stderr, "tevent_req_poll failed: %s\n",
359                         strerror(ret));
360                 TALLOC_FREE(ev);
361                 return ret;
362         }
363
364         TALLOC_FREE(req2);
365
366         ret = tevent_loop_wait(ev);
367         if (ret != 0) {
368                 fprintf(stderr, "tevent_loop_wait failed\n");
369                 return ret;
370         }
371
372         TALLOC_FREE(pool);
373         TALLOC_FREE(ev);
374         return 0;
375 }
376
377 int main(void)
378 {
379         int ret;
380
381         ret = test_tevent_1();
382         if (ret != 0) {
383                 fprintf(stderr, "test_event_1 failed: %s\n",
384                         strerror(ret));
385                 return 1;
386         }
387
388         ret = test_init();
389         if (ret != 0) {
390                 fprintf(stderr, "test_init failed\n");
391                 return 1;
392         }
393
394         ret = test_fork();
395         if (ret != 0) {
396                 fprintf(stderr, "test_fork failed\n");
397                 return 1;
398         }
399
400         ret = test_jobs(10, 10000);
401         if (ret != 0) {
402                 fprintf(stderr, "test_jobs failed\n");
403                 return 1;
404         }
405
406         ret = test_busydestroy();
407         if (ret != 0) {
408                 fprintf(stderr, "test_busydestroy failed\n");
409                 return 1;
410         }
411
412         ret = test_busyfork();
413         if (ret != 0) {
414                 fprintf(stderr, "test_busyfork failed\n");
415                 return 1;
416         }
417
418         printf("success\n");
419         return 0;
420 }