Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2005 - 2006 Pekka Riikonen
+ Copyright (C) 2005 - 2007 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
if (f->thread && f->u.t.event)
silc_fsm_event_free(f->u.t.event);
+ if (!f->thread)
+ silc_atomic_uninit32(&f->u.m.threads);
+
silc_free(f);
}
silc_fsm_start_real_thread(f->schedule,
silc_schedule_get_context(f->schedule),
0, 0, f);
+ silc_schedule_wakeup(f->schedule);
return;
}
SilcUInt32 seconds, SilcUInt32 useconds)
{
SilcFSM f = fsm;
+
f->next_state = next_state;
if (!seconds && !useconds)
return;
+
silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f,
seconds, useconds);
f->next_later = TRUE;
+
+ /* Wakeup up the scheduler just in case this was called from another
+ thread. */
+ silc_schedule_wakeup(f->schedule);
}
/* Continue after callback or async operation */
void silc_fsm_continue(void *fsm)
{
SilcFSM f = fsm;
+
if (f->next_later) {
+ /* Cancel next_later timeout */
silc_schedule_task_del_by_all(f->schedule, 0, silc_fsm_run, f);
f->next_later = FALSE;
}
+
if (!silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f, 0, 0))
silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
+
+ /* Wakeup up the scheduler just in case this was called from another
+ thread. */
+ silc_schedule_wakeup(f->schedule);
}
/* Continue after callback or async operation immediately */
SILC_ASSERT(!f->finished);
- /* Machine must not have active threads */
- if (!f->thread && silc_atomic_get_int32(&f->u.m.threads))
- assert(silc_atomic_get_int32(&f->u.m.threads) == 0);
-
f->started = FALSE;
f->finished = TRUE;
fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
} else {
+ /* Machine must not have active threads */
+ assert(silc_atomic_get_int32(&fsm->u.m.threads) == 0);
+
if (fsm->u.m.lock) {
silc_mutex_free(fsm->u.m.lock);
fsm->u.m.lock = NULL;
}
- silc_atomic_uninit32(&fsm->u.m.threads);
/* Call the destructor callback. */
if (fsm->destructor)
SILC_LOG_DEBUG(("Received event %p", event));
- /* It is possible that this FSM is in the list so remove it */
+ /* Remove from waiting */
silc_list_del(event->waiters, fsm);
- event->value--;
+
+ /* Decrease the counter only after all waiters have acquired the signal. */
+ if (!silc_list_count(event->waiters))
+ event->value--;
+
silc_mutex_unlock(lock);
return 1;
}
{
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 sempahore is posted. */
+ when the event is signalled. */
silc_mutex_lock(lock);
if (!p->event->value) {
silc_mutex_unlock(lock);
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",
event->value++;
silc_list_start(event->waiters);
- while ((fsm = silc_list_get(event->waiters)) != SILC_LIST_END) {
+ while ((fsm = silc_list_get(event->waiters))) {
if (fsm->event) {
silc_schedule_task_del_by_all(fsm->schedule, 0, silc_fsm_event_timedout,
fsm);
silc_mutex_lock(lock);
silc_list_start(event->waiters);
- while ((fsm = silc_list_get(event->waiters)) != SILC_LIST_END) {
+ while ((fsm = silc_list_get(event->waiters))) {
/* Signal on thread termination. Wake up destination scheduler in case
caller is a real thread. */
silc_list_del(event->waiters, fsm);