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