Call silc_schedule_wakeup in silc_fsm_continue and
[crypto.git] / lib / silcutil / silcfsm.c
1 /*
2
3   silcfsm.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21
22 SILC_TASK_CALLBACK(silc_fsm_run);
23 SILC_TASK_CALLBACK(silc_fsm_finish_fsm);
24 SILC_TASK_CALLBACK(silc_fsm_event_timedout);
25 SILC_TASK_CALLBACK(silc_fsm_start_real_thread);
26 static void silc_fsm_thread_termination_signal(SilcFSMEvent event);
27 static void silc_fsm_event_ref(SilcFSMEvent event);
28 static void silc_fsm_event_unref(SilcFSMEvent event);
29 void *silc_fsm_thread(void *context);
30
31 /* Allocate FSM */
32
33 SilcFSM silc_fsm_alloc(void *fsm_context,
34                        SilcFSMDestructor destructor,
35                        void *destructor_context,
36                        SilcSchedule schedule)
37 {
38   SilcFSM fsm;
39
40   fsm = silc_calloc(1, sizeof(*fsm));
41   if (silc_unlikely(!fsm))
42     return NULL;
43
44   if (silc_unlikely(!silc_fsm_init(fsm, fsm_context, destructor,
45                                    destructor_context, schedule))) {
46     silc_free(fsm);
47     return NULL;
48   }
49
50   return fsm;
51 }
52
53 /* Initialize FSM */
54
55 SilcBool silc_fsm_init(SilcFSM fsm,
56                        void *fsm_context,
57                        SilcFSMDestructor destructor,
58                        void *destructor_context,
59                        SilcSchedule schedule)
60 {
61   if (!schedule)
62     return FALSE;
63
64   fsm->fsm_context = fsm_context;
65   fsm->state_context = NULL;
66   fsm->destructor = destructor;
67   fsm->destructor_context = destructor_context;
68   fsm->schedule = schedule;
69   fsm->thread = FALSE;
70   fsm->async_call = FALSE;
71   fsm->started = FALSE;
72   fsm->u.m.lock = NULL;
73   silc_atomic_init32(&fsm->u.m.threads, 0);
74
75   return TRUE;
76 }
77
78 /* Allocate FSM thread.  Internally machine and thread use same context. */
79
80 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
81                                     void *thread_context,
82                                     SilcFSMThreadDestructor destructor,
83                                     void *destructor_context,
84                                     SilcBool real_thread)
85 {
86   SilcFSMThread thread;
87
88   thread = silc_calloc(1, sizeof(*thread));
89   if (silc_unlikely(!thread))
90     return NULL;
91
92   silc_fsm_thread_init(thread, fsm, thread_context, destructor,
93                        destructor_context, real_thread);
94   return thread;
95 }
96
97 /* Initialize FSM thread.  Internally machine and thread use same context. */
98
99 void silc_fsm_thread_init(SilcFSMThread thread,
100                           SilcFSM fsm,
101                           void *thread_context,
102                           SilcFSMThreadDestructor destructor,
103                           void *destructor_context,
104                           SilcBool real_thread)
105 {
106   SILC_LOG_DEBUG(("Initializing new thread %p (%s)",
107                   thread, real_thread ? "real" : "FSM"));
108
109   SILC_ASSERT(!fsm->thread);
110
111   thread->fsm_context = thread_context;
112   thread->state_context = NULL;
113   thread->destructor = (SilcFSMDestructor)destructor;
114   thread->destructor_context = destructor_context;
115   thread->schedule = fsm->schedule;
116   thread->thread = TRUE;
117   thread->async_call = FALSE;
118   thread->started = FALSE;
119   thread->real_thread = real_thread;
120   thread->u.t.fsm = fsm;
121
122   /* Add to machine */
123   silc_atomic_add_int32(&fsm->u.m.threads, 1);
124
125   /* Allocate lock for the machine if using real threads. */
126   if (real_thread && !fsm->u.m.lock)
127     if (!silc_mutex_alloc(&fsm->u.m.lock))
128       thread->real_thread = FALSE;
129 }
130
131 /* FSM is destroyed through scheduler to make sure that all dying
132    real system threads will have their finish callbacks scheduled before
133    this one (when SILC_FSM_THREAD_WAIT was used). */
134
135 SILC_TASK_CALLBACK(silc_fsm_free_final)
136 {
137   SilcFSM f = context;
138
139 #if defined(SILC_DEBUG)
140   /* We must be finished */
141   SILC_ASSERT(f->finished);
142
143   /* Machine must not have active threads */
144   if (!f->thread && silc_atomic_get_int32(&f->u.m.threads))
145     SILC_ASSERT(silc_atomic_get_int32(&f->u.m.threads) == 0);
146 #endif /* SILC_DEBUG */
147
148   if (!f->thread && f->u.m.lock)
149     silc_mutex_free(f->u.m.lock);
150
151   if (f->thread && f->u.t.event)
152     silc_fsm_event_free(f->u.t.event);
153
154   silc_free(f);
155 }
156
157 /* Free FSM */
158
159 void silc_fsm_free(void *fsm)
160 {
161   SilcFSM f = fsm;
162   if (!f->thread)
163     if (silc_schedule_task_add_timeout(f->schedule, silc_fsm_free_final,
164                                        f, 0, 0))
165       return;
166   silc_fsm_free_final(f->schedule, silc_schedule_get_context(f->schedule),
167                       0, 0, f);
168 }
169
170 /* Task to start real thread. We start threads through scheduler, not
171    directly in silc_fsm_start. */
172
173 SILC_TASK_CALLBACK(silc_fsm_start_real_thread)
174 {
175   SilcFSM f = context;
176
177 #ifdef SILC_THREADS
178   if (silc_thread_create(silc_fsm_thread, f, FALSE))
179     return;
180 #endif /* SILC_THREADS */
181
182   SILC_LOG_DEBUG(("Could not create real thread, using normal FSM thread"));
183
184   /* Normal FSM operation */
185   f->real_thread = FALSE;
186   silc_fsm_continue_sync(f);
187 }
188
189 /* Start FSM in the specified state */
190
191 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state)
192 {
193   SilcFSM f = fsm;
194
195   SILC_LOG_DEBUG(("Starting %s %p", f->thread ? "thread" : "FSM", fsm));
196
197   f->finished = FALSE;
198   f->next_state = start_state;
199   f->synchronous = FALSE;
200   f->started = TRUE;
201
202   /* Start real thread through scheduler */
203   if (f->thread && f->real_thread) {
204     if (!silc_schedule_task_add_timeout(f->schedule,
205                                         silc_fsm_start_real_thread,
206                                         f, 0, 0))
207       silc_fsm_start_real_thread(f->schedule,
208                                  silc_schedule_get_context(f->schedule),
209                                  0, 0, f);
210     silc_schedule_wakeup(f->schedule);
211     return;
212   }
213
214   /* Normal FSM operation */
215   if (!silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f, 0, 0))
216     silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
217
218   /* Wakeup scheduler in case we are starting this thread from another
219      real thread. */
220   if (f->thread)
221     silc_schedule_wakeup(f->schedule);
222 }
223
224 /* Start FSM in the specified state synchronously */
225
226 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state)
227 {
228   SilcFSM f = fsm;
229
230   SILC_LOG_DEBUG(("Starting %s %p", f->thread ? "thread" : "FSM", fsm));
231
232   f->finished = FALSE;
233   f->next_state = start_state;
234   f->synchronous = TRUE;
235   f->started = TRUE;
236
237   /* Start real thread directly */
238   if (f->thread && f->real_thread) {
239     silc_fsm_start_real_thread(f->schedule,
240                                silc_schedule_get_context(f->schedule),
241                                0, 0, f);
242     return;
243   }
244
245   /* Normal FSM operation */
246   silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
247 }
248
249 /* Set next FSM state */
250
251 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state)
252 {
253   SilcFSM f = fsm;
254   f->next_state = next_state;
255 }
256
257 /* Continue after timeout */
258
259 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
260                          SilcUInt32 seconds, SilcUInt32 useconds)
261 {
262   SilcFSM f = fsm;
263
264   f->next_state = next_state;
265   if (!seconds && !useconds)
266     return;
267
268   silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f,
269                                  seconds, useconds);
270   f->next_later = TRUE;
271
272   /* Wakeup up the scheduler just in case this was called from another
273      thread. */
274   silc_schedule_wakeup(f->schedule);
275 }
276
277 /* Continue after callback or async operation */
278
279 void silc_fsm_continue(void *fsm)
280 {
281   SilcFSM f = fsm;
282
283   if (f->next_later) {
284     /* Cancel next_later timeout */
285     silc_schedule_task_del_by_all(f->schedule, 0, silc_fsm_run, f);
286     f->next_later = FALSE;
287   }
288
289   if (!silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f, 0, 0))
290     silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
291
292   /* Wakeup up the scheduler just in case this was called from another
293      thread. */
294   silc_schedule_wakeup(f->schedule);
295 }
296
297 /* Continue after callback or async operation immediately */
298
299 void silc_fsm_continue_sync(void *fsm)
300 {
301   SilcFSM f = fsm;
302   if (f->next_later) {
303     silc_schedule_task_del_by_all(f->schedule, 0, silc_fsm_run, f);
304     f->next_later = FALSE;
305   }
306   silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
307 }
308
309 /* Finish FSM */
310
311 void silc_fsm_finish(void *fsm)
312 {
313   SilcFSM f = fsm;
314
315   SILC_ASSERT(!f->finished);
316
317   /* Machine must not have active threads */
318   if (!f->thread && silc_atomic_get_int32(&f->u.m.threads))
319     assert(silc_atomic_get_int32(&f->u.m.threads) == 0);
320
321   f->started = FALSE;
322   f->finished = TRUE;
323
324   silc_schedule_task_del_by_all(f->schedule, 0, silc_fsm_run, f);
325   f->next_later = FALSE;
326
327   /* If we are thread and using real threads, the FSM thread will finish
328      after the real thread has finished, in the main thread. */
329   if (f->thread && f->real_thread) {
330     /* Stop the real thread's scheduler to finish the thread */
331     silc_schedule_stop(f->schedule);
332     silc_schedule_wakeup(f->schedule);
333     return;
334   }
335
336   /* Normal FSM operation */
337   if (!f->synchronous)
338     if (silc_schedule_task_add_timeout(f->schedule, silc_fsm_finish_fsm,
339                                        f, 0, 0))
340       return;
341
342   silc_fsm_finish_fsm(f->schedule, silc_schedule_get_context(f->schedule),
343                       0, 0, fsm);
344 }
345
346 /* Return associated scheduler */
347
348 SilcSchedule silc_fsm_get_schedule(void *fsm)
349 {
350   SilcFSM f = fsm;
351   return f->schedule;
352 }
353
354 /* Return thread's machine */
355
356 SilcFSM silc_fsm_get_machine(SilcFSMThread thread)
357 {
358   SILC_ASSERT(thread->thread);
359   return (SilcFSM)thread->u.t.fsm;
360 }
361
362 /* Returns TRUE if FSM is started */
363
364 SilcBool silc_fsm_is_started(void *fsm)
365 {
366   SilcFSM f = fsm;
367   return f->started;
368 }
369
370 /* Set context */
371
372 void silc_fsm_set_context(void *fsm, void *fsm_context)
373 {
374   SilcFSM f = fsm;
375   f->fsm_context = fsm_context;
376 }
377
378 /* Get context */
379
380 void *silc_fsm_get_context(void *fsm)
381 {
382   SilcFSM f = fsm;
383   return f->fsm_context;
384 }
385
386 /* Set state context */
387
388 void silc_fsm_set_state_context(void *fsm, void *state_context)
389 {
390   SilcFSM f = fsm;
391   f->state_context = state_context;
392 }
393
394 /* Get state context */
395
396 void *silc_fsm_get_state_context(void *fsm)
397 {
398   SilcFSM f = fsm;
399   return f->state_context;
400 }
401
402 /* Wait for thread to terminate */
403
404 SilcBool silc_fsm_thread_wait(void *fsm, void *thread)
405 {
406   SilcFSM t = thread;
407
408   SILC_ASSERT(t->thread);
409
410   t->u.t.event = silc_fsm_event_alloc(t->u.t.fsm);
411   if (!t->u.t.event)
412     return FALSE;
413
414   SILC_LOG_DEBUG(("Waiting for thread %p to terminate", thread));
415   silc_fsm_event_wait(t->u.t.event, fsm);
416   return TRUE;
417 }
418
419 /* The machine */
420
421 SILC_TASK_CALLBACK(silc_fsm_run)
422 {
423   SilcFSM fsm = context;
424   SilcFSMStatus status;
425
426   SILC_LOG_DEBUG(("Running %s %p", fsm->thread ? "thread" : "FSM", fsm));
427
428   /* Run the states */
429   do
430     status = fsm->next_state(fsm, fsm->fsm_context, fsm->state_context);
431   while (status == SILC_FSM_ST_CONTINUE);
432
433   switch (status) {
434   case SILC_FSM_ST_YIELD:
435     /* Continue through scheduler */
436     silc_fsm_continue(fsm);
437     break;
438
439   case SILC_FSM_ST_WAIT:
440     /* The machine is in hold */
441     SILC_LOG_DEBUG(("State wait %p", fsm));
442     fsm->synchronous = FALSE;
443     break;
444
445   case SILC_FSM_ST_FINISH:
446     /* Finish the state machine */
447     SILC_LOG_DEBUG(("State finish %p", fsm));
448     silc_fsm_finish(fsm);
449     break;
450
451   default:
452     break;
453   }
454 }
455
456 /* Finishes the FSM.  This is always executed in the main thread, even
457    for FSM threads that were run in real threads. */
458
459 SILC_TASK_CALLBACK(silc_fsm_finish_fsm)
460 {
461   SilcFSM fsm = context;
462
463   SILC_LOG_DEBUG(("%s %p, is finished", fsm->thread ? "Thread" : "FSM", fsm));
464
465   fsm->next_state = NULL;
466
467   if (fsm->thread) {
468     /* This is thread, send signal */
469     if (fsm->u.t.event) {
470       silc_fsm_thread_termination_signal(fsm->u.t.event);
471       silc_fsm_event_free(fsm->u.t.event);
472       fsm->u.t.event = NULL;
473     }
474
475     /* Remove the thread from machine */
476     silc_atomic_sub_int32(&fsm->u.t.fsm->u.m.threads, 1);
477
478     /* Call the destructor callback only if the underlaying machine is
479        still valid. */
480     if (fsm->destructor && fsm->u.t.fsm->finished == FALSE)
481       fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
482
483   } else {
484     if (fsm->u.m.lock) {
485       silc_mutex_free(fsm->u.m.lock);
486       fsm->u.m.lock = NULL;
487     }
488     silc_atomic_uninit32(&fsm->u.m.threads);
489
490     /* Call the destructor callback. */
491     if (fsm->destructor)
492       fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
493   }
494 }
495
496 /* Allocate FSM event */
497
498 SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm)
499 {
500   SilcFSMEvent event;
501
502   event = silc_calloc(1, sizeof(*event));
503   if (silc_unlikely(!event))
504     return NULL;
505
506   silc_fsm_event_init(event, fsm);
507   event->allocated = TRUE;
508
509   return event;
510 }
511
512 /* Initializes FSM event */
513
514 void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm)
515 {
516   SILC_LOG_DEBUG(("Initializing event %p", event));
517   SILC_ASSERT(!fsm->thread);
518   memset(event, 0, sizeof(*event));
519   event->fsm = fsm;
520   event->refcnt = 0;
521   silc_list_init(event->waiters, struct SilcFSMObject, next);
522 }
523
524 /* Free event */
525
526 void silc_fsm_event_free(SilcFSMEvent event)
527 {
528   if (event->refcnt > 0)
529     return;
530   if (silc_list_count(event->waiters) > 0)
531     return;
532   silc_free(event);
533 }
534
535 /* Reference event */
536
537 static void silc_fsm_event_ref(SilcFSMEvent event)
538 {
539   event->refcnt++;
540 }
541
542 /* Unreference event */
543
544 static void silc_fsm_event_unref(SilcFSMEvent event)
545 {
546   event->refcnt--;
547   if (event->refcnt == 0 && event->allocated)
548     silc_fsm_event_free(event);
549 }
550
551 /* Wait until event is non-zero. */
552
553 SilcUInt32 silc_fsm_event_wait(SilcFSMEvent event, void *fsm)
554 {
555   SilcMutex lock = event->fsm->u.m.lock;
556
557   silc_mutex_lock(lock);
558
559   if (!event->value) {
560 #if defined(SILC_DEBUG)
561     SilcFSM entry;
562     silc_list_start(event->waiters);
563     while ((entry = silc_list_get(event->waiters)))
564       SILC_ASSERT(entry != fsm);
565 #endif /* SILC_DEBUG */
566
567     SILC_LOG_DEBUG(("Waiting for event %p", event));
568
569     /* Add the FSM to waiter list */
570     silc_list_add(event->waiters, fsm);
571     silc_mutex_unlock(lock);
572     return 0;
573   }
574
575   SILC_LOG_DEBUG(("Received event %p", event));
576
577   /* Remove from waiting */
578   silc_list_del(event->waiters, fsm);
579
580   /* Decrease the counter only after all waiters have acquired the signal. */
581   if (!silc_list_count(event->waiters))
582     event->value--;
583
584   silc_mutex_unlock(lock);
585   return 1;
586 }
587
588 /* Wait util event is non-zero, or timeout occurs. */
589
590 SilcUInt32 silc_fsm_event_timedwait(SilcFSMEvent event, void *fsm,
591                                     SilcUInt32 seconds, SilcUInt32 useconds,
592                                     SilcBool *ret_to)
593 {
594   SilcMutex lock = event->fsm->u.m.lock;
595   SilcFSM f = fsm;
596   SilcUInt32 value;
597
598   silc_mutex_lock(lock);
599
600   if (f->event_timedout) {
601     SILC_LOG_DEBUG(("Event waiting timedout"));
602     f->event_timedout = FALSE;
603     if (ret_to)
604       *ret_to = TRUE;
605     silc_mutex_unlock(lock);
606     return 1;
607   }
608
609   silc_mutex_unlock(lock);
610
611   value = silc_fsm_event_wait(event, fsm);
612   if (!value) {
613     silc_schedule_task_add_timeout(f->schedule, silc_fsm_event_timedout,
614                                    f, seconds, useconds);
615     f->event = event;
616   }
617
618   if (ret_to)
619     *ret_to = FALSE;
620
621   return value;
622 }
623
624 /* Event timedout */
625
626 SILC_TASK_CALLBACK(silc_fsm_event_timedout)
627 {
628   SilcFSM fsm = context;
629   SilcMutex lock = fsm->event->fsm->u.m.lock;
630
631   SILC_LOG_DEBUG(("Event %p timedout", fsm->event));
632
633   /* Remove the waiter from the event waiters list */
634   silc_mutex_lock(lock);
635   silc_list_del(fsm->event->waiters, fsm);
636
637   /* Continue */
638   if (fsm->event) {
639     silc_fsm_continue(fsm);
640     fsm->event_timedout = TRUE;
641     fsm->event = NULL;
642   }
643
644   silc_mutex_unlock(lock);
645 }
646
647 /* Signalled, event */
648
649 SILC_TASK_CALLBACK(silc_fsm_signal)
650 {
651   SilcFSMEventSignal p = context;
652   SilcMutex lock = p->event->fsm->u.m.lock;
653   SilcFSM fsm;
654
655   /* We have to check for couple of things before delivering the signal. */
656
657   /* If the event value has went to zero while we've been waiting this
658      callback, the event has been been signalled already.  It can happen
659      when using real threads because the FSM may not be in waiting state
660      when the event is signalled. */
661   silc_mutex_lock(lock);
662   if (!p->event->value) {
663     silc_mutex_unlock(lock);
664     silc_fsm_event_unref(p->event);
665     silc_free(p);
666     return;
667   }
668
669   /* If the waiter is not waiting anymore, don't deliver the signal.  It
670      can happen if there were multiple signallers and the waiter went away
671      after the first signal. */
672   silc_list_start(p->event->waiters);
673   while ((fsm = silc_list_get(p->event->waiters)))
674     if (fsm == p->fsm)
675       break;
676   if (!fsm) {
677     silc_mutex_unlock(lock);
678     silc_fsm_event_unref(p->event);
679     silc_free(p);
680     return;
681   }
682   silc_mutex_unlock(lock);
683
684   SILC_LOG_DEBUG(("Signalled %s %p", p->fsm->thread ? "thread" : "FSM",
685                   p->fsm));
686
687   /* Signal */
688   silc_fsm_continue_sync(p->fsm);
689
690   silc_fsm_event_unref(p->event);
691   silc_free(p);
692 }
693
694 /* Signal event */
695
696 void silc_fsm_event_signal(SilcFSMEvent event)
697 {
698   SilcFSM fsm;
699   SilcFSMEventSignal p;
700   SilcMutex lock = event->fsm->u.m.lock;
701
702   SILC_LOG_DEBUG(("Signal event %p", event));
703
704   silc_mutex_lock(lock);
705
706   event->value++;
707   silc_list_start(event->waiters);
708   while ((fsm = silc_list_get(event->waiters))) {
709     if (fsm->event) {
710       silc_schedule_task_del_by_all(fsm->schedule, 0, silc_fsm_event_timedout,
711                                     fsm);
712       fsm->event = NULL;
713     }
714
715     p = silc_calloc(1, sizeof(*p));
716     if (silc_unlikely(!p))
717       continue;
718     p->event = event;
719     p->fsm = fsm;
720     silc_fsm_event_ref(event);
721
722     /* Signal through scheduler.  Wake up destination scheduler in case
723        caller is a real thread. */
724     silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_signal, p, 0, 0);
725     silc_schedule_wakeup(fsm->schedule);
726   }
727
728   silc_mutex_unlock(lock);
729 }
730
731 /* Post thread termination event.  Special function used only to
732    signal thread termination when SILC_FSM_THREAD_WAIT was used. */
733
734 static void silc_fsm_thread_termination_signal(SilcFSMEvent event)
735 {
736   SilcFSM fsm;
737   SilcMutex lock = event->fsm->u.m.lock;
738
739   SILC_LOG_DEBUG(("Post thread terminate event %p", event));
740
741   silc_mutex_lock(lock);
742
743   silc_list_start(event->waiters);
744   while ((fsm = silc_list_get(event->waiters))) {
745     /* Signal on thread termination.  Wake up destination scheduler in case
746        caller is a real thread. */
747     silc_list_del(event->waiters, fsm);
748     silc_fsm_continue(fsm);
749     silc_schedule_wakeup(fsm->schedule);
750   }
751
752   silc_mutex_unlock(lock);
753 }
754
755 /* Real thread */
756
757 void *silc_fsm_thread(void *context)
758 {
759   SilcFSM fsm = context;
760   SilcSchedule old = fsm->schedule;
761
762   SILC_LOG_DEBUG(("Starting FSM thread in real thread"));
763
764   /* We allocate new SilcSchedule for the FSM, as the old SilcSchedule
765      cannot be used in this thread.  Application may still use it if it
766      wants but we use our own. */
767   fsm->schedule = silc_schedule_init(0, old);
768   if (silc_unlikely(!fsm->schedule))
769     return NULL;
770
771   /* Start the FSM thread */
772   if (silc_unlikely(!silc_schedule_task_add_timeout(fsm->schedule,
773                                                     silc_fsm_run, fsm, 0, 0)))
774     return NULL;
775
776   /* Run the scheduler */
777   silc_schedule(fsm->schedule);
778
779   /* Free resources */
780   silc_schedule_uninit(fsm->schedule);
781
782   fsm->schedule = old;
783
784   /* Finish the FSM thread in the main thread */
785   SILC_ASSERT(fsm->finished);
786   silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_finish_fsm,
787                                  fsm, 0, 0);
788   silc_schedule_wakeup(fsm->schedule);
789
790   return NULL;
791 }