Do not uninitialize u.m.thread atomic in in finish but in free.
[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   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   /* Machine must not have active threads */
321   if (!f->thread && silc_atomic_get_int32(&f->u.m.threads))
322     assert(silc_atomic_get_int32(&f->u.m.threads) == 0);
323
324   f->started = FALSE;
325   f->finished = TRUE;
326
327   silc_schedule_task_del_by_all(f->schedule, 0, silc_fsm_run, f);
328   f->next_later = FALSE;
329
330   /* If we are thread and using real threads, the FSM thread will finish
331      after the real thread has finished, in the main thread. */
332   if (f->thread && f->real_thread) {
333     /* Stop the real thread's scheduler to finish the thread */
334     silc_schedule_stop(f->schedule);
335     silc_schedule_wakeup(f->schedule);
336     return;
337   }
338
339   /* Normal FSM operation */
340   if (!f->synchronous)
341     if (silc_schedule_task_add_timeout(f->schedule, silc_fsm_finish_fsm,
342                                        f, 0, 0))
343       return;
344
345   silc_fsm_finish_fsm(f->schedule, silc_schedule_get_context(f->schedule),
346                       0, 0, fsm);
347 }
348
349 /* Return associated scheduler */
350
351 SilcSchedule silc_fsm_get_schedule(void *fsm)
352 {
353   SilcFSM f = fsm;
354   return f->schedule;
355 }
356
357 /* Return thread's machine */
358
359 SilcFSM silc_fsm_get_machine(SilcFSMThread thread)
360 {
361   SILC_ASSERT(thread->thread);
362   return (SilcFSM)thread->u.t.fsm;
363 }
364
365 /* Returns TRUE if FSM is started */
366
367 SilcBool silc_fsm_is_started(void *fsm)
368 {
369   SilcFSM f = fsm;
370   return f->started;
371 }
372
373 /* Set context */
374
375 void silc_fsm_set_context(void *fsm, void *fsm_context)
376 {
377   SilcFSM f = fsm;
378   f->fsm_context = fsm_context;
379 }
380
381 /* Get context */
382
383 void *silc_fsm_get_context(void *fsm)
384 {
385   SilcFSM f = fsm;
386   return f->fsm_context;
387 }
388
389 /* Set state context */
390
391 void silc_fsm_set_state_context(void *fsm, void *state_context)
392 {
393   SilcFSM f = fsm;
394   f->state_context = state_context;
395 }
396
397 /* Get state context */
398
399 void *silc_fsm_get_state_context(void *fsm)
400 {
401   SilcFSM f = fsm;
402   return f->state_context;
403 }
404
405 /* Wait for thread to terminate */
406
407 SilcBool silc_fsm_thread_wait(void *fsm, void *thread)
408 {
409   SilcFSM t = thread;
410
411   SILC_ASSERT(t->thread);
412
413   t->u.t.event = silc_fsm_event_alloc(t->u.t.fsm);
414   if (!t->u.t.event)
415     return FALSE;
416
417   SILC_LOG_DEBUG(("Waiting for thread %p to terminate", thread));
418   silc_fsm_event_wait(t->u.t.event, fsm);
419   return TRUE;
420 }
421
422 /* The machine */
423
424 SILC_TASK_CALLBACK(silc_fsm_run)
425 {
426   SilcFSM fsm = context;
427   SilcFSMStatus status;
428
429   SILC_LOG_DEBUG(("Running %s %p", fsm->thread ? "thread" : "FSM", fsm));
430
431   /* Run the states */
432   do
433     status = fsm->next_state(fsm, fsm->fsm_context, fsm->state_context);
434   while (status == SILC_FSM_ST_CONTINUE);
435
436   switch (status) {
437   case SILC_FSM_ST_YIELD:
438     /* Continue through scheduler */
439     silc_fsm_continue(fsm);
440     break;
441
442   case SILC_FSM_ST_WAIT:
443     /* The machine is in hold */
444     SILC_LOG_DEBUG(("State wait %p", fsm));
445     fsm->synchronous = FALSE;
446     break;
447
448   case SILC_FSM_ST_FINISH:
449     /* Finish the state machine */
450     SILC_LOG_DEBUG(("State finish %p", fsm));
451     silc_fsm_finish(fsm);
452     break;
453
454   default:
455     break;
456   }
457 }
458
459 /* Finishes the FSM.  This is always executed in the main thread, even
460    for FSM threads that were run in real threads. */
461
462 SILC_TASK_CALLBACK(silc_fsm_finish_fsm)
463 {
464   SilcFSM fsm = context;
465
466   SILC_LOG_DEBUG(("%s %p, is finished", fsm->thread ? "Thread" : "FSM", fsm));
467
468   fsm->next_state = NULL;
469
470   if (fsm->thread) {
471     /* This is thread, send signal */
472     if (fsm->u.t.event) {
473       silc_fsm_thread_termination_signal(fsm->u.t.event);
474       silc_fsm_event_free(fsm->u.t.event);
475       fsm->u.t.event = NULL;
476     }
477
478     /* Remove the thread from machine */
479     silc_atomic_sub_int32(&fsm->u.t.fsm->u.m.threads, 1);
480
481     /* Call the destructor callback only if the underlaying machine is
482        still valid. */
483     if (fsm->destructor && fsm->u.t.fsm->finished == FALSE)
484       fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
485
486   } else {
487     if (fsm->u.m.lock) {
488       silc_mutex_free(fsm->u.m.lock);
489       fsm->u.m.lock = NULL;
490     }
491
492     /* Call the destructor callback. */
493     if (fsm->destructor)
494       fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
495   }
496 }
497
498 /* Allocate FSM event */
499
500 SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm)
501 {
502   SilcFSMEvent event;
503
504   event = silc_calloc(1, sizeof(*event));
505   if (silc_unlikely(!event))
506     return NULL;
507
508   silc_fsm_event_init(event, fsm);
509   event->allocated = TRUE;
510
511   return event;
512 }
513
514 /* Initializes FSM event */
515
516 void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm)
517 {
518   SILC_LOG_DEBUG(("Initializing event %p", event));
519   SILC_ASSERT(!fsm->thread);
520   memset(event, 0, sizeof(*event));
521   event->fsm = fsm;
522   event->refcnt = 0;
523   silc_list_init(event->waiters, struct SilcFSMObject, next);
524 }
525
526 /* Free event */
527
528 void silc_fsm_event_free(SilcFSMEvent event)
529 {
530   if (event->refcnt > 0)
531     return;
532   if (silc_list_count(event->waiters) > 0)
533     return;
534   silc_free(event);
535 }
536
537 /* Reference event */
538
539 static void silc_fsm_event_ref(SilcFSMEvent event)
540 {
541   event->refcnt++;
542 }
543
544 /* Unreference event */
545
546 static void silc_fsm_event_unref(SilcFSMEvent event)
547 {
548   event->refcnt--;
549   if (event->refcnt == 0 && event->allocated)
550     silc_fsm_event_free(event);
551 }
552
553 /* Wait until event is non-zero. */
554
555 SilcUInt32 silc_fsm_event_wait(SilcFSMEvent event, void *fsm)
556 {
557   SilcMutex lock = event->fsm->u.m.lock;
558
559   silc_mutex_lock(lock);
560
561   if (!event->value) {
562 #if defined(SILC_DEBUG)
563     SilcFSM entry;
564     silc_list_start(event->waiters);
565     while ((entry = silc_list_get(event->waiters)))
566       SILC_ASSERT(entry != fsm);
567 #endif /* SILC_DEBUG */
568
569     SILC_LOG_DEBUG(("Waiting for event %p", event));
570
571     /* Add the FSM to waiter list */
572     silc_list_add(event->waiters, fsm);
573     silc_mutex_unlock(lock);
574     return 0;
575   }
576
577   SILC_LOG_DEBUG(("Received event %p", event));
578
579   /* Remove from waiting */
580   silc_list_del(event->waiters, fsm);
581
582   /* Decrease the counter only after all waiters have acquired the signal. */
583   if (!silc_list_count(event->waiters))
584     event->value--;
585
586   silc_mutex_unlock(lock);
587   return 1;
588 }
589
590 /* Wait util event is non-zero, or timeout occurs. */
591
592 SilcUInt32 silc_fsm_event_timedwait(SilcFSMEvent event, void *fsm,
593                                     SilcUInt32 seconds, SilcUInt32 useconds,
594                                     SilcBool *ret_to)
595 {
596   SilcMutex lock = event->fsm->u.m.lock;
597   SilcFSM f = fsm;
598   SilcUInt32 value;
599
600   silc_mutex_lock(lock);
601
602   if (f->event_timedout) {
603     SILC_LOG_DEBUG(("Event waiting timedout"));
604     f->event_timedout = FALSE;
605     if (ret_to)
606       *ret_to = TRUE;
607     silc_mutex_unlock(lock);
608     return 1;
609   }
610
611   silc_mutex_unlock(lock);
612
613   value = silc_fsm_event_wait(event, fsm);
614   if (!value) {
615     silc_schedule_task_add_timeout(f->schedule, silc_fsm_event_timedout,
616                                    f, seconds, useconds);
617     f->event = event;
618   }
619
620   if (ret_to)
621     *ret_to = FALSE;
622
623   return value;
624 }
625
626 /* Event timedout */
627
628 SILC_TASK_CALLBACK(silc_fsm_event_timedout)
629 {
630   SilcFSM fsm = context;
631   SilcMutex lock = fsm->event->fsm->u.m.lock;
632
633   SILC_LOG_DEBUG(("Event %p timedout", fsm->event));
634
635   /* Remove the waiter from the event waiters list */
636   silc_mutex_lock(lock);
637   silc_list_del(fsm->event->waiters, fsm);
638
639   /* Continue */
640   if (fsm->event) {
641     silc_fsm_continue(fsm);
642     fsm->event_timedout = TRUE;
643     fsm->event = NULL;
644   }
645
646   silc_mutex_unlock(lock);
647 }
648
649 /* Signalled, event */
650
651 SILC_TASK_CALLBACK(silc_fsm_signal)
652 {
653   SilcFSMEventSignal p = context;
654   SilcMutex lock = p->event->fsm->u.m.lock;
655   SilcFSM fsm;
656
657   /* We have to check for couple of things before delivering the signal. */
658
659   /* If the event value has went to zero while we've been waiting this
660      callback, the event has been been signalled already.  It can happen
661      when using real threads because the FSM may not be in waiting state
662      when the event is signalled. */
663   silc_mutex_lock(lock);
664   if (!p->event->value) {
665     silc_mutex_unlock(lock);
666     silc_fsm_event_unref(p->event);
667     silc_free(p);
668     return;
669   }
670
671   /* If the waiter is not waiting anymore, don't deliver the signal.  It
672      can happen if there were multiple signallers and the waiter went away
673      after the first signal. */
674   silc_list_start(p->event->waiters);
675   while ((fsm = silc_list_get(p->event->waiters)))
676     if (fsm == p->fsm)
677       break;
678   if (!fsm) {
679     silc_mutex_unlock(lock);
680     silc_fsm_event_unref(p->event);
681     silc_free(p);
682     return;
683   }
684   silc_mutex_unlock(lock);
685
686   SILC_LOG_DEBUG(("Signalled %s %p", p->fsm->thread ? "thread" : "FSM",
687                   p->fsm));
688
689   /* Signal */
690   silc_fsm_continue_sync(p->fsm);
691
692   silc_fsm_event_unref(p->event);
693   silc_free(p);
694 }
695
696 /* Signal event */
697
698 void silc_fsm_event_signal(SilcFSMEvent event)
699 {
700   SilcFSM fsm;
701   SilcFSMEventSignal p;
702   SilcMutex lock = event->fsm->u.m.lock;
703
704   SILC_LOG_DEBUG(("Signal event %p", event));
705
706   silc_mutex_lock(lock);
707
708   event->value++;
709   silc_list_start(event->waiters);
710   while ((fsm = silc_list_get(event->waiters))) {
711     if (fsm->event) {
712       silc_schedule_task_del_by_all(fsm->schedule, 0, silc_fsm_event_timedout,
713                                     fsm);
714       fsm->event = NULL;
715     }
716
717     p = silc_calloc(1, sizeof(*p));
718     if (silc_unlikely(!p))
719       continue;
720     p->event = event;
721     p->fsm = fsm;
722     silc_fsm_event_ref(event);
723
724     /* Signal through scheduler.  Wake up destination scheduler in case
725        caller is a real thread. */
726     silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_signal, p, 0, 0);
727     silc_schedule_wakeup(fsm->schedule);
728   }
729
730   silc_mutex_unlock(lock);
731 }
732
733 /* Post thread termination event.  Special function used only to
734    signal thread termination when SILC_FSM_THREAD_WAIT was used. */
735
736 static void silc_fsm_thread_termination_signal(SilcFSMEvent event)
737 {
738   SilcFSM fsm;
739   SilcMutex lock = event->fsm->u.m.lock;
740
741   SILC_LOG_DEBUG(("Post thread terminate event %p", event));
742
743   silc_mutex_lock(lock);
744
745   silc_list_start(event->waiters);
746   while ((fsm = silc_list_get(event->waiters))) {
747     /* Signal on thread termination.  Wake up destination scheduler in case
748        caller is a real thread. */
749     silc_list_del(event->waiters, fsm);
750     silc_fsm_continue(fsm);
751     silc_schedule_wakeup(fsm->schedule);
752   }
753
754   silc_mutex_unlock(lock);
755 }
756
757 /* Real thread */
758
759 void *silc_fsm_thread(void *context)
760 {
761   SilcFSM fsm = context;
762   SilcSchedule old = fsm->schedule;
763
764   SILC_LOG_DEBUG(("Starting FSM thread in real thread"));
765
766   /* We allocate new SilcSchedule for the FSM, as the old SilcSchedule
767      cannot be used in this thread.  Application may still use it if it
768      wants but we use our own. */
769   fsm->schedule = silc_schedule_init(0, old);
770   if (silc_unlikely(!fsm->schedule))
771     return NULL;
772
773   /* Start the FSM thread */
774   if (silc_unlikely(!silc_schedule_task_add_timeout(fsm->schedule,
775                                                     silc_fsm_run, fsm, 0, 0)))
776     return NULL;
777
778   /* Run the scheduler */
779   silc_schedule(fsm->schedule);
780
781   /* Free resources */
782   silc_schedule_uninit(fsm->schedule);
783
784   fsm->schedule = old;
785
786   /* Finish the FSM thread in the main thread */
787   SILC_ASSERT(fsm->finished);
788   silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_finish_fsm,
789                                  fsm, 0, 0);
790   silc_schedule_wakeup(fsm->schedule);
791
792   return NULL;
793 }