1 /*
2
3 silcfsm.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2005 - 2007 Pekka Riikonen
8
9 The contents of this file are subject to one of the Licenses specified
10 in the COPYING file; You may not use this file except in compliance
11 with the License.
12
13 The software distributed under the License is distributed on an "AS IS"
14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15 KIND, either expressed or implied. See the COPYING file for more
16 information.
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
silc_fsm_alloc(void * fsm_context,SilcFSMDestructor destructor,void * destructor_context,SilcSchedule schedule)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
silc_fsm_init(SilcFSM fsm,void * fsm_context,SilcFSMDestructor destructor,void * destructor_context,SilcSchedule schedule)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
silc_fsm_thread_alloc(SilcFSM fsm,void * thread_context,SilcFSMThreadDestructor destructor,void * destructor_context,SilcBool real_thread)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
silc_fsm_thread_init(SilcFSMThread thread,SilcFSM fsm,void * thread_context,SilcFSMThreadDestructor destructor,void * destructor_context,SilcBool real_thread)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
SILC_TASK_CALLBACK(silc_fsm_free_final)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
silc_fsm_free(void * fsm)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
SILC_TASK_CALLBACK(silc_fsm_start_real_thread)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
silc_fsm_start(void * fsm,SilcFSMStateCallback start_state)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
silc_fsm_start_sync(void * fsm,SilcFSMStateCallback start_state)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
silc_fsm_next(void * fsm,SilcFSMStateCallback next_state)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
silc_fsm_next_later(void * fsm,SilcFSMStateCallback next_state,SilcUInt32 seconds,SilcUInt32 useconds)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
silc_fsm_continue(void * fsm)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
silc_fsm_continue_sync(void * fsm)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
silc_fsm_finish(void * fsm)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
silc_fsm_get_schedule(void * fsm)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
silc_fsm_get_machine(SilcFSMThread thread)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
silc_fsm_is_started(void * fsm)363 SilcBool silc_fsm_is_started(void *fsm)
364 {
365 SilcFSM f = fsm;
366 return f->started;
367 }
368
369 /* Set context */
370
silc_fsm_set_context(void * fsm,void * fsm_context)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
silc_fsm_get_context(void * fsm)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
silc_fsm_set_state_context(void * fsm,void * state_context)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
silc_fsm_get_state_context(void * fsm)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
silc_fsm_thread_wait(void * fsm,void * thread)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
SILC_TASK_CALLBACK(silc_fsm_run)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
SILC_TASK_CALLBACK(silc_fsm_finish_fsm)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
silc_fsm_event_alloc(SilcFSM fsm)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
silc_fsm_event_init(SilcFSMEvent event,SilcFSM fsm)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
silc_fsm_event_free(SilcFSMEvent event)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
silc_fsm_event_ref(SilcFSMEvent event)538 static void silc_fsm_event_ref(SilcFSMEvent event)
539 {
540 event->refcnt++;
541 }
542
543 /* Unreference event */
544
silc_fsm_event_unref(SilcFSMEvent event)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
silc_fsm_event_wait(SilcFSMEvent event,void * fsm)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
silc_fsm_event_timedwait(SilcFSMEvent event,void * fsm,SilcUInt32 seconds,SilcUInt32 useconds,SilcBool * ret_to)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
SILC_TASK_CALLBACK(silc_fsm_event_timedout)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
SILC_TASK_CALLBACK(silc_fsm_signal)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
silc_fsm_event_signal(SilcFSMEvent event)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
silc_fsm_thread_termination_signal(SilcFSMEvent event)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
silc_fsm_thread(void * context)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 }
793