Added asynchronous event tasks to SILC Scheduler. Added
[crypto.git] / lib / silcutil / silcfsm.c
index aff9a77edf5e1cf8d7c053083289e06fcf706475..2ded983253aa77906cd439210b69306afe92116c 100644 (file)
@@ -58,8 +58,13 @@ SilcBool silc_fsm_init(SilcFSM fsm,
                       void *destructor_context,
                       SilcSchedule schedule)
 {
-  if (!schedule)
-    return FALSE;
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return FALSE;
+    }
+  }
 
   fsm->fsm_context = fsm_context;
   fsm->state_context = NULL;
@@ -106,7 +111,7 @@ void silc_fsm_thread_init(SilcFSMThread thread,
   SILC_LOG_DEBUG(("Initializing new thread %p (%s)",
                  thread, real_thread ? "real" : "FSM"));
 
-  SILC_ASSERT(!fsm->thread);
+  SILC_VERIFY(!fsm->thread);
 
   thread->fsm_context = thread_context;
   thread->state_context = NULL;
@@ -151,6 +156,9 @@ SILC_TASK_CALLBACK(silc_fsm_free_final)
   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);
 }
 
@@ -207,6 +215,7 @@ void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state)
       silc_fsm_start_real_thread(f->schedule,
                                 silc_schedule_get_context(f->schedule),
                                 0, 0, f);
+    silc_schedule_wakeup(f->schedule);
     return;
   }
 
@@ -259,12 +268,18 @@ void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
                         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 */
@@ -272,12 +287,19 @@ void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
 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 */
@@ -298,11 +320,7 @@ void silc_fsm_finish(void *fsm)
 {
   SilcFSM f = fsm;
 
-  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);
+  SILC_VERIFY(!f->finished);
 
   f->started = FALSE;
   f->finished = TRUE;
@@ -341,7 +359,7 @@ SilcSchedule silc_fsm_get_schedule(void *fsm)
 
 SilcFSM silc_fsm_get_machine(SilcFSMThread thread)
 {
-  SILC_ASSERT(thread->thread);
+  SILC_VERIFY(thread->thread);
   return (SilcFSM)thread->u.t.fsm;
 }
 
@@ -391,7 +409,7 @@ SilcBool silc_fsm_thread_wait(void *fsm, void *thread)
 {
   SilcFSM t = thread;
 
-  SILC_ASSERT(t->thread);
+  SILC_VERIFY(t->thread);
 
   t->u.t.event = silc_fsm_event_alloc(t->u.t.fsm);
   if (!t->u.t.event)
@@ -467,11 +485,13 @@ SILC_TASK_CALLBACK(silc_fsm_finish_fsm)
       fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
 
   } else {
+    /* Machine must not have active threads */
+    SILC_VERIFY(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)
@@ -500,7 +520,7 @@ SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm)
 void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm)
 {
   SILC_LOG_DEBUG(("Initializing event %p", event));
-  SILC_ASSERT(!fsm->thread);
+  SILC_VERIFY(!fsm->thread);
   memset(event, 0, sizeof(*event));
   event->fsm = fsm;
   event->refcnt = 0;
@@ -560,9 +580,13 @@ SilcUInt32 silc_fsm_event_wait(SilcFSMEvent event, void *fsm)
 
   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;
 }
@@ -634,12 +658,12 @@ SILC_TASK_CALLBACK(silc_fsm_signal)
   SilcMutex lock = p->event->fsm->u.m.lock;
   SilcFSM fsm;
 
-  /* We have to check couple of things before delivering the signal. */
+  /* 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);
@@ -648,7 +672,9 @@ SILC_TASK_CALLBACK(silc_fsm_signal)
     return;
   }
 
-  /* If the waiter is not waiting anymore, don't deliver the signal */
+  /* 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)
@@ -685,7 +711,7 @@ void silc_fsm_event_signal(SilcFSMEvent event)
 
   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);
@@ -744,18 +770,29 @@ void *silc_fsm_thread(void *context)
   /* We allocate new SilcSchedule for the FSM, as the old SilcSchedule
      cannot be used in this thread.  Application may still use it if it
      wants but we use our own. */
-  fsm->schedule = silc_schedule_init(0, old);
-  if (silc_unlikely(!fsm->schedule))
+  fsm->schedule = silc_schedule_init(0, old, silc_schedule_get_stack(old), old);
+  if (silc_unlikely(!fsm->schedule)) {
+    fsm->schedule = old;
     return NULL;
+  }
+
+  /* The new scheduler is a global scheduler in this thread */
+  silc_schedule_set_global(fsm->schedule);
 
   /* Start the FSM thread */
   if (silc_unlikely(!silc_schedule_task_add_timeout(fsm->schedule,
-                                                   silc_fsm_run, fsm, 0, 0)))
+                                                   silc_fsm_run, fsm, 0, 0))) {
+    silc_schedule_uninit(fsm->schedule);
+    fsm->schedule = old;
     return NULL;
+  }
 
   /* Run the scheduler */
   silc_schedule(fsm->schedule);
 
+  /* Reset global scheduler */
+  silc_schedule_set_global(NULL);
+
   /* Free resources */
   silc_schedule_uninit(fsm->schedule);