Added SILC Thread Queue API
[silc.git] / lib / silcutil / tests / test_silcfsm.c
1 /* SILC FSM tests */
2
3 #include "silc.h"
4 #include "silcfsm.h"
5
6 typedef void (*Callback)(void *context);
7
8 #define NUM_THREADS 200
9
10 typedef struct FooStruct *Foo;
11
12 typedef struct {
13   SilcFSMThreadStruct thread;
14   SilcFSMEventStruct sema;
15   SilcBool finished;
16   int rounds;
17   Foo f;
18 } T;
19
20 struct FooStruct {
21   SilcBool error;
22   SilcFSM fsm;
23   SilcFSMThreadStruct thread;
24   int timeout;
25   SilcFSMEventStruct sema;
26   SilcFSMEventStruct wait2;
27   SilcSchedule schedule;
28   Callback cb;
29   void *cb_context;
30   T threads[NUM_THREADS];
31   T threads2[NUM_THREADS];
32   int c;
33   int got_wait1 : 1;
34   int got_wait2 : 1;
35 };
36
37 SILC_FSM_STATE(test_st_start);
38 SILC_FSM_STATE(test_st_second);
39 SILC_FSM_STATE(test_st_second_timeout);
40 SILC_FSM_STATE(test_st_third);
41 SILC_FSM_STATE(test_st_fourth);
42 SILC_FSM_STATE(test_st_fifth);
43 SILC_FSM_STATE(test_st_sixth);
44 SILC_FSM_STATE(test_st_seventh);
45 SILC_FSM_STATE(test_st_eighth);
46 SILC_FSM_STATE(test_st_ninth);
47 SILC_FSM_STATE(test_st_tenth);
48 SILC_FSM_STATE(test_st_finish);
49
50 SILC_FSM_STATE(test_st_wait1);
51 SILC_FSM_STATE(test_st_wait2);
52 SILC_FSM_STATE(test_st_signal1);
53 SILC_FSM_STATE(test_st_signal1_check);
54
55 SILC_FSM_STATE(test_thread_st_start);
56 SILC_FSM_STATE(test_thread_st_finish);
57 SILC_FSM_STATE(test_thread2_st_start);
58 SILC_FSM_STATE(test_thread2_st_finish);
59 SILC_FSM_STATE(test_thread3_st_start);
60 SILC_FSM_STATE(test_thread4_st_start);
61
62 static void test_fsm_destr(SilcFSMThread thread, void *thread_context,
63                            void *user_context)
64 {
65   silc_fsm_free(thread);
66 }
67
68 SILC_TASK_CALLBACK(async_call_timeout)
69 {
70   Foo f = context;
71   SILC_LOG_DEBUG(("Async call cb, continuing FSM"));
72   f->cb(f->cb_context);
73 }
74
75 static void async_call(Callback cb, void *context)
76 {
77   Foo f = context;
78   f->cb = cb;
79   f->cb_context = context;
80   SILC_LOG_DEBUG(("Async call"));
81   silc_schedule_task_add(f->schedule, 0, async_call_timeout, f, 0, 200000,
82                          SILC_TASK_TIMEOUT);
83 }
84
85 SILC_FSM_STATE(test_st_start)
86 {
87   SILC_LOG_DEBUG(("test_st_start"));
88
89   /** Move to second state */
90   SILC_LOG_DEBUG(("Move to next state"));
91   silc_fsm_next(fsm, test_st_second);
92   return SILC_FSM_CONTINUE;
93 }
94
95 SILC_FSM_STATE(test_st_second)
96 {
97   SILC_LOG_DEBUG(("test_st_second"));
98
99   /** Move to second timeout state, timeout */
100   SILC_LOG_DEBUG(("Move to next state with 2 second timeout"));
101   silc_fsm_next_later(fsm, test_st_second_timeout, 2, 0);
102   return SILC_FSM_WAIT;
103 }
104
105 SILC_TASK_CALLBACK(test_second_timeout)
106 {
107   Foo f = context;
108   SILC_LOG_DEBUG(("test_second_timeout"));
109
110   SILC_LOG_DEBUG(("Interrupt 3 second wait and continue immediately"));
111   f->c++;
112   silc_fsm_next(f->fsm, test_st_third);
113   silc_fsm_continue(f->fsm);
114 }
115
116 SILC_FSM_STATE(test_st_second_timeout)
117 {
118   Foo f = fsm_context;
119
120   SILC_LOG_DEBUG(("test_st_second_timeout"));
121
122   /** Move to third state, timeout */
123   SILC_LOG_DEBUG(("Move to next state with 3 second timeout"));
124   SILC_LOG_DEBUG(("The timeout will be interrupted with silc_fsm_continue"));
125   silc_fsm_next_later(fsm, test_st_third, 3, 0);
126   silc_schedule_task_add_timeout(silc_fsm_get_schedule(fsm),
127                                  test_second_timeout, f, 2, 500000);
128   return SILC_FSM_WAIT;
129 }
130
131 static void async_call_cb(void *context)
132 {
133   Foo f = context;
134   SILC_LOG_DEBUG(("Callback, continue to next state"));
135   SILC_FSM_CALL_CONTINUE(f->fsm);
136 }
137
138 SILC_FSM_STATE(test_st_third)
139 {
140   Foo f = fsm_context;
141
142   SILC_LOG_DEBUG(("test_st_third"));
143
144   f->c++;
145   assert(f->c == 2);
146
147   f->fsm = fsm;
148
149   /** Wait async callback*/
150   SILC_LOG_DEBUG(("Call async call"));
151   silc_fsm_next(fsm, test_st_fourth);
152   SILC_FSM_CALL(async_call(async_call_cb, f));
153 }
154
155 SILC_FSM_STATE(test_st_fourth)
156 {
157   Foo f = fsm_context;
158   SilcFSMThread t;
159
160   SILC_LOG_DEBUG(("test_st_fourth"));
161
162   f->timeout = 1;
163
164   SILC_LOG_DEBUG(("Creating FSM thread"));
165   silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, FALSE);
166   SILC_LOG_DEBUG(("Starting thread"));
167   /*** Start thread */
168   silc_fsm_start(&f->thread, test_thread_st_start);
169
170   SILC_LOG_DEBUG(("Creating two waiting threads"));
171   silc_fsm_event_init(&f->wait2, fsm);
172   t = silc_fsm_thread_alloc(fsm, f, test_fsm_destr, NULL, FALSE);
173   silc_fsm_start(t, test_st_wait1);
174   t = silc_fsm_thread_alloc(fsm, f, test_fsm_destr, NULL, FALSE);
175   silc_fsm_start(t, test_st_wait2);
176
177   SILC_LOG_DEBUG(("Create signaller thread"));
178   t = silc_fsm_thread_alloc(fsm, f, test_fsm_destr, NULL, FALSE);
179   silc_fsm_start(t, test_st_signal1);
180
181   /** Waiting thread to terminate */
182   SILC_LOG_DEBUG(("Waiting for thread to terminate"));
183   silc_fsm_next(fsm, test_st_fifth);
184   SILC_FSM_THREAD_WAIT(&f->thread);
185 }
186
187 SILC_FSM_STATE(test_st_wait1)
188 {
189   Foo f = fsm_context;
190
191   SILC_LOG_DEBUG(("Waiter 1"));
192   SILC_FSM_EVENT_WAIT(&f->wait2);
193   SILC_LOG_DEBUG(("Waiter 1 signalled"));
194   f->got_wait1 = 1;
195   return SILC_FSM_FINISH;
196 }
197
198 SILC_FSM_STATE(test_st_wait2)
199 {
200   Foo f = fsm_context;
201
202   SILC_LOG_DEBUG(("Waiter 2"));
203   SILC_FSM_EVENT_WAIT(&f->wait2);
204   SILC_LOG_DEBUG(("Waiter 2 signalled"));
205   f->got_wait2 = 1;
206   return SILC_FSM_FINISH;
207 }
208
209 SILC_FSM_STATE(test_st_signal1)
210 {
211   Foo f = fsm_context;
212
213   SILC_LOG_DEBUG(("Signaller 1"));
214   SILC_FSM_EVENT_SIGNAL(&f->wait2);
215   silc_fsm_next_later(fsm, test_st_signal1_check, 0, 500000);
216   return SILC_FSM_WAIT;;
217 }
218
219 SILC_FSM_STATE(test_st_signal1_check)
220 {
221   Foo f = fsm_context;
222
223   SILC_LOG_DEBUG(("Signal check"));
224   assert(f->got_wait1 && f->got_wait2);
225   return SILC_FSM_FINISH;
226 }
227
228 SILC_FSM_STATE(test_thread_st_start)
229 {
230   Foo f = fsm_context;
231
232   SILC_LOG_DEBUG(("test_thread_st_start"));
233
234   /** Move to final state, timeout */
235   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
236   silc_fsm_next_later(fsm, test_thread_st_finish, f->timeout, 0);
237   return SILC_FSM_WAIT;
238 }
239
240 SILC_FSM_STATE(test_thread_st_finish)
241 {
242   SILC_LOG_DEBUG(("test_thread_st_finish"));
243
244   SILC_LOG_DEBUG(("Finishing the thread"));
245   return SILC_FSM_FINISH;
246 }
247
248 SILC_FSM_STATE(test_st_fifth)
249 {
250   Foo f = fsm_context;
251   SILC_LOG_DEBUG(("test_st_fifth"));
252
253   SILC_LOG_DEBUG(("Thread terminated, start new real thread"));
254
255   f->timeout = 7;
256
257   SILC_LOG_DEBUG(("Creating FSM event"));
258   silc_fsm_event_init(&f->sema, fsm);
259
260   SILC_LOG_DEBUG(("Creating FSM thread"));
261   silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, TRUE);
262   SILC_LOG_DEBUG(("Starting thread"));
263   silc_fsm_start(&f->thread, test_thread2_st_start);
264
265   /** Waiting thread to terminate, timeout */
266   SILC_LOG_DEBUG(("Waiting for thread to terminate for 5 seconds"));
267   silc_fsm_next(fsm, test_st_sixth);
268   SILC_FSM_EVENT_TIMEDWAIT(&f->sema, 5, 0, NULL);
269   return SILC_FSM_CONTINUE;
270 }
271
272 SILC_FSM_STATE(test_thread2_st_start)
273 {
274   Foo f = fsm_context;
275
276   SILC_LOG_DEBUG(("test_thread2_st_start"));
277
278   /** Move to final state, timeout */
279   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
280   silc_fsm_next_later(fsm, test_thread2_st_finish, f->timeout, 0);
281   return SILC_FSM_WAIT;
282 }
283
284 SILC_FSM_STATE(test_thread2_st_finish)
285 {
286   Foo f = fsm_context;
287   SILC_LOG_DEBUG(("test_thread2_st_finish"));
288
289   SILC_LOG_DEBUG(("Post semaphore"));
290   SILC_FSM_EVENT_SIGNAL(&f->sema);
291
292   SILC_LOG_DEBUG(("Finishing the thread"));
293   return SILC_FSM_FINISH;
294 }
295
296 SILC_FSM_STATE(test_st_sixth)
297 {
298   SILC_LOG_DEBUG(("test_st_sixth"));
299
300   SILC_LOG_DEBUG(("Thread wait timedout, OK"));
301
302   /** Move to next state, timeout */
303   SILC_LOG_DEBUG(("Continue to next state with 4 second timeout"));
304   silc_fsm_next_later(fsm, test_st_seventh, 4, 0);
305   return SILC_FSM_WAIT;
306 }
307
308 SILC_FSM_STATE(test_thread3_st_start)
309 {
310   T *t = fsm_context;
311
312   if (t->rounds == 0) {
313     SILC_FSM_EVENT_SIGNAL(&t->sema);
314     return SILC_FSM_FINISH;
315   }
316
317   t->rounds--;
318
319   /** Call in recursive */
320   silc_fsm_next(fsm, test_thread3_st_start);
321   return SILC_FSM_CONTINUE;
322 }
323
324 SILC_FSM_STATE(test_st_seventh)
325 {
326   Foo f = fsm_context;
327   int i;
328
329   SILC_LOG_DEBUG(("test_st_seventh"));
330
331
332   SILC_LOG_DEBUG(("Creating %d FSM threads", NUM_THREADS));
333   for (i = 0; i < NUM_THREADS; i++) {
334     f->threads[i].rounds = 10;
335     f->threads[i].f = f;
336     silc_fsm_event_init(&f->threads[i].sema, fsm);
337     silc_fsm_thread_init(&f->threads[i].thread, fsm,
338                          &f->threads[i], NULL, NULL, FALSE);
339     silc_fsm_start(&f->threads[i].thread, test_thread3_st_start);
340   }
341
342   /** Move to wait threads */
343   silc_fsm_next(fsm, test_st_eighth);
344   return SILC_FSM_CONTINUE;
345 }
346
347 SILC_FSM_STATE(test_st_eighth)
348 {
349   Foo f = fsm_context;
350   int i;
351
352   for (i = 0; i < NUM_THREADS; i++) {
353     if (f->threads[i].finished == FALSE) {
354       SILC_FSM_EVENT_WAIT(&f->threads[i].sema);
355       f->threads[i].finished = TRUE;
356     }
357   }
358
359   SILC_LOG_DEBUG(("All %d threads terminated", NUM_THREADS));
360
361   /** Move to next thread */
362   silc_fsm_next(fsm, test_st_ninth);
363   return SILC_FSM_CONTINUE;
364 }
365
366 SILC_FSM_STATE(test_thread4_st_start)
367 {
368   T *t = fsm_context;
369
370   if (t->rounds == 0) {
371     SILC_FSM_EVENT_SIGNAL(&t->sema);
372     return SILC_FSM_FINISH;
373   }
374
375   t->rounds--;
376
377   /** Call in recursive */
378   silc_fsm_next(fsm, test_thread4_st_start);
379   return SILC_FSM_CONTINUE;
380 }
381
382 SILC_FSM_STATE(test_st_ninth)
383 {
384   Foo f = fsm_context;
385   int i;
386
387   SILC_LOG_DEBUG(("test_st_ninth"));
388
389   SILC_LOG_DEBUG(("Creating FSM event"));
390   silc_fsm_event_init(&f->sema, fsm);
391
392   SILC_LOG_DEBUG(("Creating %d real FSM threads", NUM_THREADS));
393   for (i = 0; i < NUM_THREADS; i++) {
394     f->threads2[i].rounds = 10;
395     f->threads2[i].f = f;
396     silc_fsm_event_init(&f->threads2[i].sema, fsm);
397     silc_fsm_thread_init(&f->threads2[i].thread, fsm,
398                          &f->threads2[i], NULL, NULL, TRUE);
399     silc_fsm_start(&f->threads2[i].thread, test_thread4_st_start);
400   }
401
402   /** Move to wait threads */
403   silc_fsm_next(fsm, test_st_tenth);
404   return SILC_FSM_CONTINUE;
405 }
406
407 SILC_FSM_STATE(test_st_tenth)
408 {
409   Foo f = fsm_context;
410   int i;
411
412   for (i = 0; i < NUM_THREADS; i++)
413     if (f->threads2[i].finished == FALSE) {
414       SILC_FSM_EVENT_WAIT(&f->threads2[i].sema);
415       f->threads2[i].finished = TRUE;
416     }
417
418   SILC_LOG_DEBUG(("All %d real threads terminated", NUM_THREADS));
419
420   /** Finished successfully */
421   silc_fsm_next_later(fsm, test_st_finish, 2, 0);
422   return SILC_FSM_WAIT;
423 }
424
425 SILC_FSM_STATE(test_st_finish)
426 {
427   SILC_LOG_DEBUG(("test_st_finish"));
428
429   SILC_LOG_DEBUG(("Finish machine"));
430   return SILC_FSM_FINISH;
431 }
432
433 static void destructor(SilcFSM fsm, void *fsm_context,
434                        void *destructor_context)
435 {
436   Foo f = destructor_context;
437   SILC_LOG_DEBUG(("FSM destructor, stopping scheduler"));
438   silc_fsm_free(fsm);
439   silc_schedule_stop(f->schedule);
440 }
441
442 int main(int argc, char **argv)
443 {
444   SilcBool success = FALSE;
445   SilcSchedule schedule;
446   SilcFSM fsm;
447   Foo f;
448
449   if (argc > 1 && !strcmp(argv[1], "-d")) {
450     silc_log_debug(TRUE);
451     silc_log_debug_hexdump(TRUE);
452     silc_log_quick(TRUE);
453     silc_log_set_debug_string("*fsm*,*async*,*errno*");
454   }
455
456   SILC_LOG_DEBUG(("Allocating scheduler"));
457   schedule = silc_schedule_init(0, NULL, NULL);
458
459   f = silc_calloc(1, sizeof(*f));
460   if (!f)
461     goto err;
462   f->schedule = schedule;
463
464   SILC_LOG_DEBUG(("Allocating FSM context"));
465   f->fsm = fsm = silc_fsm_alloc(f, destructor, f, schedule);
466   if (!fsm)
467     goto err;
468   silc_fsm_start(fsm, test_st_start);
469
470   SILC_LOG_DEBUG(("Running scheduler"));
471   silc_schedule(schedule);
472
473   if (f->error)
474     goto err;
475
476   silc_schedule_uninit(schedule);
477   silc_free(f);
478
479   success = TRUE;
480
481  err:
482   SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
483   fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
484
485   return success;
486 }