TODO: Regression test to ensure that a tevent backend can cope with separate read...
[metze/samba/wip.git] / lib / tevent / testsuite.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    testing of the events subsystem
5
6    Copyright (C) Stefan Metzmacher 2006-2009
7    Copyright (C) Jeremy Allison    2013
8
9      ** NOTE! The following LGPL license applies to the tevent
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 3 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "includes.h"
28 #include "lib/tevent/tevent.h"
29 #include "system/filesys.h"
30 #include "system/select.h"
31 #include "torture/torture.h"
32 #ifdef HAVE_PTHREAD
33 #include <pthread.h>
34 #include <assert.h>
35 #endif
36
37 static int fde_count;
38 static int fde_wcount;
39
40 static void fde_handler_readwrite(struct tevent_context *ev_ctx, struct tevent_fd *f,
41                         uint16_t flags, void *private_data)
42 {
43         int *fd = (int *)private_data;
44         char c = 0;
45 #ifdef SA_SIGINFO
46         kill(getpid(), SIGUSR1);
47 #endif
48         kill(getpid(), SIGALRM);
49
50         if ((flags & TEVENT_FD_WRITE) && (fde_wcount - fde_count < 256)) {
51                 /* Don't fill the pipe and block... */
52                 write(fd[1], &c, 1);
53                 fde_wcount++;
54         } else {
55                 read(fd[0], &c, 1);
56                 fde_count++;
57         }
58 }
59
60 static void finished_handler(struct tevent_context *ev_ctx, struct tevent_timer *te,
61                              struct timeval tval, void *private_data)
62 {
63         int *finished = (int *)private_data;
64         (*finished) = 1;
65 }
66
67 static void count_handler(struct tevent_context *ev_ctx, struct tevent_signal *te,
68                           int signum, int count, void *info, void *private_data)
69 {
70         int *countp = (int *)private_data;
71         (*countp) += count;
72 }
73
74 static bool test_event_context(struct torture_context *test,
75                                const void *test_data)
76 {
77         struct tevent_context *ev_ctx;
78         int fd[2] = { -1, -1 };
79         const char *backend = (const char *)test_data;
80         int alarm_count=0, info_count=0;
81         struct tevent_fd *fde_read;
82         struct tevent_fd *fde_read_1;
83         struct tevent_fd *fde_write;
84         struct tevent_fd *fde_write_1;
85 #ifdef SA_RESTART
86         struct tevent_signal *se1 = NULL;
87 #endif
88 #ifdef SA_RESETHAND
89         struct tevent_signal *se2 = NULL;
90 #endif
91 #ifdef SA_SIGINFO
92         struct tevent_signal *se3 = NULL;
93 #endif
94         int finished=0;
95         struct timeval t;
96
97         ev_ctx = tevent_context_init_byname(test, backend);
98         if (ev_ctx == NULL) {
99                 torture_comment(test, "event backend '%s' not supported\n", backend);
100                 return true;
101         }
102
103         torture_comment(test, "backend '%s' - %s\n",
104                         backend, __FUNCTION__);
105
106         /* reset globals */
107         fde_count = 0;
108         fde_wcount = 0;
109
110         /* create a pipe */
111         pipe(fd);
112
113         fde_read = tevent_add_fd(ev_ctx, ev_ctx, fd[0], TEVENT_FD_READ,
114                             fde_handler_readwrite, fd);
115         fde_write_1 = tevent_add_fd(ev_ctx, ev_ctx, fd[0], TEVENT_FD_WRITE,
116                             fde_handler_readwrite, fd);
117
118         fde_write = tevent_add_fd(ev_ctx, ev_ctx, fd[1], TEVENT_FD_WRITE,
119                             fde_handler_readwrite, fd);
120         fde_read_1 = tevent_add_fd(ev_ctx, ev_ctx, fd[1], TEVENT_FD_READ,
121                             fde_handler_readwrite, fd);
122
123         tevent_fd_set_auto_close(fde_read);
124         tevent_fd_set_auto_close(fde_write);
125
126         tevent_add_timer(ev_ctx, ev_ctx, timeval_current_ofs(2,0),
127                          finished_handler, &finished);
128
129 #ifdef SA_RESTART
130         se1 = tevent_add_signal(ev_ctx, ev_ctx, SIGALRM, SA_RESTART, count_handler, &alarm_count);
131 #endif
132 #ifdef SA_RESETHAND
133         se2 = tevent_add_signal(ev_ctx, ev_ctx, SIGALRM, SA_RESETHAND, count_handler, &alarm_count);
134 #endif
135 #ifdef SA_SIGINFO
136         se3 = tevent_add_signal(ev_ctx, ev_ctx, SIGUSR1, SA_SIGINFO, count_handler, &info_count);
137 #endif
138
139         t = timeval_current();
140         while (!finished) {
141                 errno = 0;
142                 if (tevent_loop_once(ev_ctx) == -1) {
143                         talloc_free(ev_ctx);
144                         torture_fail(test, talloc_asprintf(test, "Failed event loop %s\n", strerror(errno)));
145                 }
146         }
147
148         talloc_free(fde_read);
149         talloc_free(fde_write);
150         talloc_free(fde_read_1);
151         talloc_free(fde_write_1);
152
153         while (alarm_count < fde_count+1) {
154                 if (tevent_loop_once(ev_ctx) == -1) {
155                         break;
156                 }
157         }
158
159         torture_comment(test, "Got %.2f pipe events/sec\n", fde_count/timeval_elapsed(&t));
160
161 #ifdef SA_RESTART
162         talloc_free(se1);
163 #endif
164
165         torture_assert_int_equal(test, alarm_count, 1+fde_count+fde_wcount, "alarm count mismatch");
166
167 #ifdef SA_RESETHAND
168         talloc_free(se2);
169 #endif
170
171 #ifdef SA_SIGINFO
172         talloc_free(se3);
173         torture_assert_int_equal(test, info_count, fde_count+fde_wcount, "info count mismatch");
174 #endif
175
176         talloc_free(ev_ctx);
177
178         return true;
179 }
180
181 #ifdef HAVE_PTHREAD
182
183 static pthread_mutex_t threaded_mutex = PTHREAD_MUTEX_INITIALIZER;
184 static bool do_shutdown = false;
185
186 static void test_event_threaded_lock(void)
187 {
188         int ret;
189         ret = pthread_mutex_lock(&threaded_mutex);
190         assert(ret == 0);
191 }
192
193 static void test_event_threaded_unlock(void)
194 {
195         int ret;
196         ret = pthread_mutex_unlock(&threaded_mutex);
197         assert(ret == 0);
198 }
199
200 static void test_event_threaded_trace(enum tevent_trace_point point,
201                                       void *private_data)
202 {
203         switch (point) {
204         case TEVENT_TRACE_BEFORE_WAIT:
205                 test_event_threaded_unlock();
206                 break;
207         case TEVENT_TRACE_AFTER_WAIT:
208                 test_event_threaded_lock();
209                 break;
210         }
211 }
212
213 static void test_event_threaded_timer(struct tevent_context *ev,
214                                       struct tevent_timer *te,
215                                       struct timeval current_time,
216                                       void *private_data)
217 {
218         return;
219 }
220
221 static void *test_event_poll_thread(void *private_data)
222 {
223         struct tevent_context *ev = (struct tevent_context *)private_data;
224
225         test_event_threaded_lock();
226
227         while (true) {
228                 int ret;
229                 ret = tevent_loop_once(ev);
230                 assert(ret == 0);
231                 if (do_shutdown) {
232                         test_event_threaded_unlock();
233                         return NULL;
234                 }
235         }
236
237 }
238
239 static void test_event_threaded_read_handler(struct tevent_context *ev,
240                                              struct tevent_fd *fde,
241                                              uint16_t flags,
242                                              void *private_data)
243 {
244         int *pfd = (int *)private_data;
245         char c;
246         ssize_t nread;
247
248         if ((flags & TEVENT_FD_READ) == 0) {
249                 return;
250         }
251
252         do {
253                 nread = read(*pfd, &c, 1);
254         } while ((nread == -1) && (errno == EINTR));
255
256         assert(nread == 1);
257 }
258
259 static bool test_event_context_threaded(struct torture_context *test,
260                                         const void *test_data)
261 {
262         struct tevent_context *ev;
263         struct tevent_timer *te;
264         struct tevent_fd *fde;
265         pthread_t poll_thread;
266         int fds[2];
267         int ret;
268         char c = 0;
269
270         ev = tevent_context_init_byname(test, "poll_mt");
271         torture_assert(test, ev != NULL, "poll_mt not supported");
272
273         tevent_set_trace_callback(ev, test_event_threaded_trace, NULL);
274
275         te = tevent_add_timer(ev, ev, timeval_current_ofs(5, 0),
276                               test_event_threaded_timer, NULL);
277         torture_assert(test, te != NULL, "Could not add timer");
278
279         ret = pthread_create(&poll_thread, NULL, test_event_poll_thread, ev);
280         torture_assert(test, ret == 0, "Could not create poll thread");
281
282         ret = pipe(fds);
283         torture_assert(test, ret == 0, "Could not create pipe");
284
285         poll(NULL, 0, 100);
286
287         test_event_threaded_lock();
288
289         fde = tevent_add_fd(ev, ev, fds[0], TEVENT_FD_READ,
290                             test_event_threaded_read_handler, &fds[0]);
291         torture_assert(test, fde != NULL, "Could not add fd event");
292
293         test_event_threaded_unlock();
294
295         poll(NULL, 0, 100);
296
297         write(fds[1], &c, 1);
298
299         poll(NULL, 0, 100);
300
301         test_event_threaded_lock();
302         do_shutdown = true;
303         test_event_threaded_unlock();
304
305         write(fds[1], &c, 1);
306
307         ret = pthread_join(poll_thread, NULL);
308         torture_assert(test, ret == 0, "pthread_join failed");
309
310         return true;
311 }
312
313 #endif
314
315 struct torture_suite *torture_local_event(TALLOC_CTX *mem_ctx)
316 {
317         struct torture_suite *suite = torture_suite_create(mem_ctx, "event");
318         const char **list = tevent_backend_list(suite);
319         int i;
320
321         for (i=0;list && list[i];i++) {
322                 torture_suite_add_simple_tcase_const(suite, list[i],
323                                                test_event_context,
324                                                (const void *)list[i]);
325         }
326
327 #ifdef HAVE_PTHREAD
328         torture_suite_add_simple_tcase_const(suite, "poll_mt_threaded",
329                                              test_event_context_threaded,
330                                              NULL);
331 #endif
332
333         return suite;
334 }