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