+SILC_TASK_CALLBACK(silc_fsm_signal)
+{
+ SilcFSMEventSignal p = context;
+ SilcMutex lock = p->event->fsm->u.m.lock;
+ SilcFSM fsm;
+
+ /* We have to check for couple of things before delivering the signal. */
+
+ /* If the event value has went to zero while we've been waiting this
+ callback, the event has been been signalled already. It can happen
+ when using real threads because the FSM may not be in waiting state
+ when the event is signalled. */
+ silc_mutex_lock(lock);
+ if (!p->event->value) {
+ silc_mutex_unlock(lock);
+ silc_fsm_event_unref(p->event);
+ silc_free(p);
+ return;
+ }
+
+ /* If the waiter is not waiting anymore, don't deliver the signal. It
+ can happen if there were multiple signallers and the waiter went away
+ after the first signal. */
+ silc_list_start(p->event->waiters);
+ while ((fsm = silc_list_get(p->event->waiters)))
+ if (fsm == p->fsm)
+ break;
+ if (!fsm) {
+ silc_mutex_unlock(lock);
+ silc_fsm_event_unref(p->event);
+ silc_free(p);
+ return;
+ }
+ silc_mutex_unlock(lock);
+
+ SILC_LOG_DEBUG(("Signalled %s %p", p->fsm->thread ? "thread" : "FSM",
+ p->fsm));
+
+ /* Signal */
+ silc_fsm_continue_sync(p->fsm);
+
+ silc_fsm_event_unref(p->event);
+ silc_free(p);