s4: torture: libsmbclient: Add a torture test to ensure smbc_stat() returns ENOENT...
[Samba.git] / lib / pthreadpool / tests.c
blob08cb59ecf592ff1cdc1ce4550d3c384df2208103
1 #include <stdio.h>
2 #include <string.h>
3 #include <poll.h>
4 #include <errno.h>
5 #include <stdlib.h>
6 #include <limits.h>
7 #include <pthread.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <signal.h>
12 #include "pthreadpool_pipe.h"
13 #include "pthreadpool_tevent.h"
15 static int test_init(void)
17 struct pthreadpool_pipe *p;
18 int ret;
20 ret = pthreadpool_pipe_init(1, &p);
21 if (ret != 0) {
22 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
23 strerror(ret));
24 return -1;
26 ret = pthreadpool_pipe_destroy(p);
27 if (ret != 0) {
28 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
29 strerror(ret));
30 return -1;
32 return 0;
35 static void test_sleep(void *ptr)
37 int *ptimeout = (int *)ptr;
38 int ret;
39 ret = poll(NULL, 0, *ptimeout);
40 if (ret != 0) {
41 fprintf(stderr, "poll returned %d (%s)\n",
42 ret, strerror(errno));
46 static int test_jobs(int num_threads, int num_jobs)
48 char *finished;
49 struct pthreadpool_pipe *p;
50 int timeout = 1;
51 int i, ret;
53 finished = (char *)calloc(1, num_jobs);
54 if (finished == NULL) {
55 fprintf(stderr, "calloc failed\n");
56 return -1;
59 ret = pthreadpool_pipe_init(num_threads, &p);
60 if (ret != 0) {
61 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
62 strerror(ret));
63 free(finished);
64 return -1;
67 for (i=0; i<num_jobs; i++) {
68 ret = pthreadpool_pipe_add_job(p, i, test_sleep, &timeout);
69 if (ret != 0) {
70 fprintf(stderr, "pthreadpool_pipe_add_job failed: "
71 "%s\n", strerror(ret));
72 free(finished);
73 return -1;
77 for (i=0; i<num_jobs; i++) {
78 int jobid = -1;
79 ret = pthreadpool_pipe_finished_jobs(p, &jobid, 1);
80 if (ret < 0) {
81 fprintf(stderr, "pthreadpool_pipe_finished_jobs "
82 "failed: %s\n", strerror(-ret));
83 free(finished);
84 return -1;
86 if ((ret != 1) || (jobid >= num_jobs)) {
87 fprintf(stderr, "invalid job number %d\n", jobid);
88 free(finished);
89 return -1;
91 finished[jobid] += 1;
94 for (i=0; i<num_jobs; i++) {
95 if (finished[i] != 1) {
96 fprintf(stderr, "finished[%d] = %d\n",
97 i, finished[i]);
98 free(finished);
99 return -1;
103 ret = pthreadpool_pipe_destroy(p);
104 if (ret != 0) {
105 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
106 strerror(ret));
107 free(finished);
108 return -1;
111 free(finished);
112 return 0;
115 static int test_busydestroy(void)
117 struct pthreadpool_pipe *p;
118 int timeout = 50;
119 struct pollfd pfd;
120 int ret, jobid;
122 ret = pthreadpool_pipe_init(1, &p);
123 if (ret != 0) {
124 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
125 strerror(ret));
126 return -1;
128 ret = pthreadpool_pipe_add_job(p, 1, test_sleep, &timeout);
129 if (ret != 0) {
130 fprintf(stderr, "pthreadpool_pipe_add_job failed: %s\n",
131 strerror(ret));
132 return -1;
134 ret = pthreadpool_pipe_destroy(p);
135 if (ret != EBUSY) {
136 fprintf(stderr, "Could destroy a busy pool\n");
137 return -1;
140 pfd.fd = pthreadpool_pipe_signal_fd(p);
141 pfd.events = POLLIN|POLLERR;
143 do {
144 ret = poll(&pfd, 1, -1);
145 } while ((ret == -1) && (errno == EINTR));
147 ret = pthreadpool_pipe_finished_jobs(p, &jobid, 1);
148 if (ret < 0) {
149 fprintf(stderr, "pthreadpool_pipe_finished_jobs failed: %s\n",
150 strerror(-ret));
151 return -1;
154 ret = pthreadpool_pipe_destroy(p);
155 if (ret != 0) {
156 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
157 strerror(ret));
158 return -1;
160 return 0;
163 static int test_fork(void)
165 struct pthreadpool_pipe *p;
166 pid_t child, waited;
167 int status, ret;
169 ret = pthreadpool_pipe_init(1, &p);
170 if (ret != 0) {
171 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
172 strerror(ret));
173 return -1;
175 ret = pthreadpool_pipe_destroy(p);
176 if (ret != 0) {
177 fprintf(stderr, "pthreadpool_pipe_destroy failed: %s\n",
178 strerror(ret));
179 return -1;
182 child = fork();
183 if (child < 0) {
184 perror("fork failed");
185 return -1;
187 if (child == 0) {
188 exit(0);
190 waited = wait(&status);
191 if (waited == -1) {
192 perror("wait failed");
193 return -1;
195 if (waited != child) {
196 fprintf(stderr, "expected child %d, got %d\n",
197 (int)child, (int)waited);
198 return -1;
200 return 0;
203 static void busyfork_job(void *private_data)
205 return;
208 static int test_busyfork(void)
210 struct pthreadpool_pipe *p;
211 int fds[2];
212 struct pollfd pfd;
213 pid_t child, waitret;
214 int ret, jobnum, wstatus;
216 ret = pipe(fds);
217 if (ret == -1) {
218 perror("pipe failed");
219 return -1;
222 ret = pthreadpool_pipe_init(1, &p);
223 if (ret != 0) {
224 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
225 strerror(ret));
226 return -1;
229 ret = pthreadpool_pipe_add_job(p, 1, busyfork_job, NULL);
230 if (ret != 0) {
231 fprintf(stderr, "pthreadpool_add_job failed: %s\n",
232 strerror(ret));
233 return -1;
236 ret = pthreadpool_pipe_finished_jobs(p, &jobnum, 1);
237 if (ret != 1) {
238 fprintf(stderr, "pthreadpool_pipe_finished_jobs failed\n");
239 return -1;
242 ret = poll(NULL, 0, 200);
243 if (ret == -1) {
244 perror("poll failed");
245 return -1;
248 child = fork();
249 if (child < 0) {
250 perror("fork failed");
251 return -1;
254 if (child == 0) {
255 ret = pthreadpool_pipe_destroy(p);
256 if (ret != 0) {
257 fprintf(stderr, "pthreadpool_pipe_destroy failed: "
258 "%s\n", strerror(ret));
259 exit(1);
261 exit(0);
264 ret = close(fds[1]);
265 if (ret == -1) {
266 perror("close failed");
267 return -1;
270 pfd = (struct pollfd) { .fd = fds[0], .events = POLLIN };
272 ret = poll(&pfd, 1, 5000);
273 if (ret == -1) {
274 perror("poll failed");
275 return -1;
277 if (ret == 0) {
278 fprintf(stderr, "Child did not exit for 5 seconds\n");
280 * The child might hang forever in
281 * pthread_cond_destroy for example. Be kind to the
282 * system and kill it.
284 kill(child, SIGTERM);
285 return -1;
287 if (ret != 1) {
288 fprintf(stderr, "poll returned %d -- huh??\n", ret);
289 return -1;
292 ret = poll(NULL, 0, 200);
293 if (ret == -1) {
294 perror("poll failed");
295 return -1;
298 waitret = waitpid(child, &wstatus, WNOHANG);
299 if (waitret != child) {
300 fprintf(stderr, "waitpid returned %d\n", (int)waitret);
301 return -1;
304 if (!WIFEXITED(wstatus)) {
305 fprintf(stderr, "child did not properly exit\n");
306 return -1;
309 ret = WEXITSTATUS(wstatus);
310 if (ret != 0) {
311 fprintf(stderr, "child returned %d\n", ret);
312 return -1;
315 return 0;
318 static int test_busyfork2(void)
320 struct pthreadpool_pipe *p;
321 pid_t child;
322 int ret, jobnum;
323 struct pollfd pfd;
325 ret = pthreadpool_pipe_init(1, &p);
326 if (ret != 0) {
327 fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
328 strerror(ret));
329 return -1;
332 ret = pthreadpool_pipe_add_job(p, 1, busyfork_job, NULL);
333 if (ret != 0) {
334 fprintf(stderr, "pthreadpool_add_job failed: %s\n",
335 strerror(ret));
336 return -1;
339 ret = pthreadpool_pipe_finished_jobs(p, &jobnum, 1);
340 if (ret != 1) {
341 fprintf(stderr, "pthreadpool_pipe_finished_jobs failed\n");
342 return -1;
345 ret = poll(NULL, 0, 10);
346 if (ret == -1) {
347 perror("poll failed");
348 return -1;
351 ret = pthreadpool_pipe_add_job(p, 1, busyfork_job, NULL);
352 if (ret != 0) {
353 fprintf(stderr, "pthreadpool_add_job failed: %s\n",
354 strerror(ret));
355 return -1;
359 * Do the fork right after the add_job. This tests a race
360 * where the atfork prepare handler gets all idle threads off
361 * the condvar. If we are faster doing the fork than the
362 * existing idle thread could get out of idle and take the
363 * job, after the fork we end up with no threads to take care
364 * of the job.
367 child = fork();
368 if (child < 0) {
369 perror("fork failed");
370 return -1;
373 if (child == 0) {
374 exit(0);
377 pfd = (struct pollfd) {
378 .fd = pthreadpool_pipe_signal_fd(p),
379 .events = POLLIN|POLLERR
382 do {
383 ret = poll(&pfd, 1, 5000);
384 } while ((ret == -1) && (errno == EINTR));
386 if (ret == 0) {
387 fprintf(stderr, "job unfinished after 5 seconds\n");
388 return -1;
391 return 0;
394 static void test_tevent_wait(void *private_data)
396 int *timeout = private_data;
397 poll(NULL, 0, *timeout);
400 static int test_tevent_1(void)
402 struct tevent_context *ev;
403 struct pthreadpool_tevent *pool;
404 struct tevent_req *req1, *req2;
405 int timeout10 = 10;
406 int timeout100 = 100;
407 int ret;
408 bool ok;
410 ev = tevent_context_init(NULL);
411 if (ev == NULL) {
412 ret = errno;
413 fprintf(stderr, "tevent_context_init failed: %s\n",
414 strerror(ret));
415 return ret;
417 ret = pthreadpool_tevent_init(ev, UINT_MAX, &pool);
418 if (ret != 0) {
419 fprintf(stderr, "pthreadpool_tevent_init failed: %s\n",
420 strerror(ret));
421 TALLOC_FREE(ev);
422 return ret;
424 req1 = pthreadpool_tevent_job_send(
425 ev, ev, pool, test_tevent_wait, &timeout10);
426 if (req1 == NULL) {
427 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
428 TALLOC_FREE(ev);
429 return ENOMEM;
431 req2 = pthreadpool_tevent_job_send(
432 ev, ev, pool, test_tevent_wait, &timeout100);
433 if (req2 == NULL) {
434 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
435 TALLOC_FREE(ev);
436 return ENOMEM;
438 ok = tevent_req_poll(req2, ev);
439 if (!ok) {
440 ret = errno;
441 fprintf(stderr, "tevent_req_poll failed: %s\n",
442 strerror(ret));
443 TALLOC_FREE(ev);
444 return ret;
446 ret = pthreadpool_tevent_job_recv(req1);
447 TALLOC_FREE(req1);
448 if (ret != 0) {
449 fprintf(stderr, "tevent_req_poll failed: %s\n",
450 strerror(ret));
451 TALLOC_FREE(ev);
452 return ret;
455 TALLOC_FREE(req2);
457 ret = tevent_loop_wait(ev);
458 if (ret != 0) {
459 fprintf(stderr, "tevent_loop_wait failed\n");
460 return ret;
463 TALLOC_FREE(pool);
464 TALLOC_FREE(ev);
465 return 0;
468 int main(void)
470 int ret;
472 ret = test_tevent_1();
473 if (ret != 0) {
474 fprintf(stderr, "test_event_1 failed: %s\n",
475 strerror(ret));
476 return 1;
479 ret = test_init();
480 if (ret != 0) {
481 fprintf(stderr, "test_init failed\n");
482 return 1;
485 ret = test_fork();
486 if (ret != 0) {
487 fprintf(stderr, "test_fork failed\n");
488 return 1;
491 ret = test_jobs(10, 10000);
492 if (ret != 0) {
493 fprintf(stderr, "test_jobs failed\n");
494 return 1;
497 ret = test_busydestroy();
498 if (ret != 0) {
499 fprintf(stderr, "test_busydestroy failed\n");
500 return 1;
503 ret = test_busyfork();
504 if (ret != 0) {
505 fprintf(stderr, "test_busyfork failed\n");
506 return 1;
509 ret = test_busyfork2();
510 if (ret != 0) {
511 fprintf(stderr, "test_busyfork2 failed\n");
512 return 1;
515 printf("success\n");
516 return 0;