Added SILC errno API. Added SilcResult, generic error code and
[silc.git] / lib / silcutil / silcfsm.c
index 0098bf2f551ade169311ca8b438769be5e09719c..3cef6d969506a8f374feb54fdcb5e3a89029b0cf 100644 (file)
@@ -58,8 +58,10 @@ SilcBool silc_fsm_init(SilcFSM fsm,
                       void *destructor_context,
                       SilcSchedule schedule)
 {
-  if (!schedule)
+  if (!schedule) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     return FALSE;
+  }
 
   fsm->fsm_context = fsm_context;
   fsm->state_context = NULL;
@@ -151,6 +153,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 +212,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 +265,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 +284,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 */
@@ -300,10 +319,6 @@ void silc_fsm_finish(void *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);
-
   f->started = FALSE;
   f->finished = TRUE;
 
@@ -467,11 +482,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)
@@ -750,14 +767,19 @@ 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));
+  if (silc_unlikely(!fsm->schedule)) {
+    fsm->schedule = old;
     return NULL;
+  }
 
   /* 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);