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