Added silc_likely and silc_unlikely GCC branch prediction macros.
[silc.git] / lib / silcutil / silcfsm.c
index 2c95ccd19f110fdba9503f376b1508d7cd079f95..4feffdffba0f74110f3d13f929278dcf49b8336e 100644 (file)
@@ -20,7 +20,7 @@
 #include "silc.h"
 
 SILC_TASK_CALLBACK(silc_fsm_run);
-SILC_TASK_CALLBACK(silc_fsm_finish);
+SILC_TASK_CALLBACK(silc_fsm_finish_fsm);
 SILC_TASK_CALLBACK(silc_fsm_sema_timedout);
 SILC_TASK_CALLBACK(silc_fsm_start_real_thread);
 static void *silc_fsm_thread(void *context);
@@ -38,11 +38,11 @@ SilcFSM silc_fsm_alloc(void *fsm_context,
   SilcFSM fsm;
 
   fsm = silc_calloc(1, sizeof(*fsm));
-  if (!fsm)
+  if (silc_unlikely(!fsm))
     return NULL;
 
-  if (!silc_fsm_init(fsm, fsm_context, destructor,
-                    destructor_context, schedule)) {
+  if (silc_unlikely(!silc_fsm_init(fsm, fsm_context, destructor,
+                                  destructor_context, schedule))) {
     silc_free(fsm);
     return NULL;
   }
@@ -68,6 +68,7 @@ SilcBool silc_fsm_init(SilcFSM fsm,
   fsm->schedule = schedule;
   fsm->thread = FALSE;
   fsm->async_call = FALSE;
+  fsm->started = FALSE;
   fsm->u.m.threads = 0;
   fsm->u.m.lock = NULL;
 
@@ -85,7 +86,7 @@ SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
   SilcFSMThread thread;
 
   thread = silc_calloc(1, sizeof(*thread));
-  if (!thread)
+  if (silc_unlikely(!thread))
     return NULL;
 
   silc_fsm_thread_init(thread, fsm, thread_context, destructor,
@@ -105,9 +106,7 @@ void silc_fsm_thread_init(SilcFSMThread thread,
   SILC_LOG_DEBUG(("Initializing new thread %p (%s)",
                  thread, real_thread ? "real" : "FSM"));
 
-#if defined(SILC_DEBUG)
   SILC_ASSERT(!fsm->thread);
-#endif /* SILC_DEBUG */
 
   thread->fsm_context = thread_context;
   thread->state_context = NULL;
@@ -116,6 +115,7 @@ void silc_fsm_thread_init(SilcFSMThread thread,
   thread->schedule = fsm->schedule;
   thread->thread = TRUE;
   thread->async_call = FALSE;
+  thread->started = FALSE;
   thread->real_thread = real_thread;
   thread->u.t.fsm = fsm;
 
@@ -159,7 +159,12 @@ SILC_TASK_CALLBACK(silc_fsm_free_final)
 void silc_fsm_free(void *fsm)
 {
   SilcFSM f = fsm;
-  silc_schedule_task_add_timeout(f->schedule, silc_fsm_free_final, f, 0, 1);
+  if (!f->thread)
+    if (silc_schedule_task_add_timeout(f->schedule, silc_fsm_free_final,
+                                      f, 0, 0))
+      return;
+  silc_fsm_free_final(f->schedule, silc_schedule_get_context(f->schedule),
+                     0, 0, f);
 }
 
 /* Task to start real thread. We start threads through scheduler, not
@@ -192,16 +197,22 @@ void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state)
   f->finished = FALSE;
   f->next_state = start_state;
   f->synchronous = FALSE;
+  f->started = TRUE;
 
   /* Start real thread through scheduler */
   if (f->thread && f->real_thread) {
-    silc_schedule_task_add_timeout(f->schedule, silc_fsm_start_real_thread,
-                                  f, 0, 1);
+    if (!silc_schedule_task_add_timeout(f->schedule,
+                                       silc_fsm_start_real_thread,
+                                       f, 0, 0))
+      silc_fsm_start_real_thread(f->schedule,
+                                silc_schedule_get_context(f->schedule),
+                                0, 0, f);
     return;
   }
 
   /* Normal FSM operation */
-  silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f, 0, 1);
+  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);
 }
 
 /* Start FSM in the specified state synchronously */
@@ -215,6 +226,7 @@ void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state)
   f->finished = FALSE;
   f->next_state = start_state;
   f->synchronous = TRUE;
+  f->started = TRUE;
 
   /* Start real thread directly */
   if (f->thread && f->real_thread) {
@@ -247,6 +259,7 @@ void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
     return;
   silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f,
                                 seconds, useconds);
+  f->next_later = TRUE;
 }
 
 /* Continue after callback or async operation */
@@ -254,7 +267,12 @@ void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
 void silc_fsm_continue(void *fsm)
 {
   SilcFSM f = fsm;
-  silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f, 0, 1);
+  if (f->next_later) {
+    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);
 }
 
 /* Continue after callback or async operation immediately */
@@ -262,9 +280,50 @@ void silc_fsm_continue(void *fsm)
 void silc_fsm_continue_sync(void *fsm)
 {
   SilcFSM f = fsm;
+  if (f->next_later) {
+    silc_schedule_task_del_by_all(f->schedule, 0, silc_fsm_run, f);
+    f->next_later = FALSE;
+  }
   silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
 }
 
+/* Finish FSM */
+
+void silc_fsm_finish(void *fsm)
+{
+  SilcFSM f = fsm;
+
+  SILC_ASSERT(!f->finished);
+
+  /* Machine must not have active threads */
+  if (!f->thread && f->u.m.threads)
+    assert(f->u.m.threads == 0);
+
+  f->started = FALSE;
+  f->finished = TRUE;
+
+  silc_schedule_task_del_by_all(f->schedule, 0, silc_fsm_run, f);
+  f->next_later = FALSE;
+
+  /* If we are thread and using real threads, the FSM thread will finish
+     after the real thread has finished, in the main thread. */
+  if (f->thread && f->real_thread) {
+    /* Stop the real thread's scheduler to finish the thread */
+    silc_schedule_stop(f->schedule);
+    silc_schedule_wakeup(f->schedule);
+    return;
+  }
+
+  /* Normal FSM operation */
+  if (!f->synchronous)
+    if (silc_schedule_task_add_timeout(f->schedule, silc_fsm_finish_fsm,
+                                      f, 0, 0))
+      return;
+
+  silc_fsm_finish_fsm(f->schedule, silc_schedule_get_context(f->schedule),
+                     0, 0, fsm);
+}
+
 /* Return associated scheduler */
 
 SilcSchedule silc_fsm_get_schedule(void *fsm)
@@ -281,6 +340,14 @@ SilcFSM silc_fsm_get_machine(SilcFSMThread thread)
   return (SilcFSM)thread->u.t.fsm;
 }
 
+/* Returns TRUE if FSM is started */
+
+SilcBool silc_fsm_is_started(void *fsm)
+{
+  SilcFSM f = fsm;
+  return f->started;
+}
+
 /* Set context */
 
 void silc_fsm_set_context(void *fsm, void *fsm_context)
@@ -319,12 +386,8 @@ SilcBool silc_fsm_thread_wait(void *fsm, void *thread)
 {
   SilcFSM t = thread;
 
-#if defined(SILC_DEBUG)
   SILC_ASSERT(t->thread);
-#endif /* SILC_DEBUG */
 
-  if (t->finished)
-    return FALSE;
   t->u.t.sema = silc_fsm_sema_alloc(t->u.t.fsm, 0);
   if (!t->u.t.sema)
     return FALSE;
@@ -363,26 +426,7 @@ SILC_TASK_CALLBACK(silc_fsm_run)
   case SILC_FSM_FINISH:
     /* Finish the state machine */
     SILC_LOG_DEBUG(("State finish %p", fsm));
-#if defined(SILC_DEBUG)
-    SILC_ASSERT(!fsm->finished);
-#endif /* SILC_DEBUG */
-    fsm->finished = TRUE;
-
-    /* If we are thread and using real threads, the FSM thread will finish
-       in the main thread, not in the created thread. */
-    if (fsm->thread && fsm->real_thread) {
-      silc_schedule_stop(fsm->schedule);
-      silc_schedule_task_add_timeout(app_context, silc_fsm_finish, fsm, 0, 1);
-      silc_schedule_wakeup(app_context);
-      break;
-    }
-
-    /* Normal FSM operation */
-    if (fsm->synchronous)
-      silc_fsm_finish(fsm->schedule, app_context, 0, 0, fsm);
-    else
-      silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_finish,
-                                    fsm, 0, 1);
+    silc_fsm_finish(fsm);
     break;
 
   default:
@@ -393,7 +437,7 @@ SILC_TASK_CALLBACK(silc_fsm_run)
 /* Finishes the FSM.  This is always executed in the main thread, even
    for FSM threads that were run in real threads. */
 
-SILC_TASK_CALLBACK(silc_fsm_finish)
+SILC_TASK_CALLBACK(silc_fsm_finish_fsm)
 {
   SilcFSM fsm = context;
 
@@ -436,7 +480,7 @@ SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value)
   SilcFSMSema sema;
 
   sema = silc_calloc(1, sizeof(*sema));
-  if (!sema)
+  if (silc_unlikely(!sema))
     return NULL;
 
   silc_fsm_sema_init(sema, fsm, value);
@@ -450,9 +494,7 @@ SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value)
 void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value)
 {
   SILC_LOG_DEBUG(("Initializing semaphore %p", sema));
-#if defined(SILC_DEBUG)
   SILC_ASSERT(!fsm->thread);
-#endif /* SILC_DEBUG */
   memset(sema, 0, sizeof(*sema));
   sema->fsm = fsm;
   sema->refcnt = 0;
@@ -631,7 +673,7 @@ void silc_fsm_sema_post(SilcFSMSema sema)
     }
 
     p = silc_calloc(1, sizeof(*p));
-    if (!p)
+    if (silc_unlikely(!p))
       continue;
     p->sema = sema;
     p->fsm = fsm;
@@ -639,7 +681,7 @@ void silc_fsm_sema_post(SilcFSMSema sema)
 
     /* Signal through scheduler.  Wake up destination scheduler in case
        caller is a real thread. */
-    silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_signal, p, 0, 1);
+    silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_signal, p, 0, 0);
     silc_schedule_wakeup(fsm->schedule);
   }
 
@@ -683,11 +725,12 @@ static void *silc_fsm_thread(void *context)
      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 (!fsm->schedule)
+  if (silc_unlikely(!fsm->schedule))
     return NULL;
 
   /* Start the FSM thread */
-  if (!silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_run, fsm, 0, 1))
+  if (silc_unlikely(!silc_schedule_task_add_timeout(fsm->schedule,
+                                                   silc_fsm_run, fsm, 0, 0)))
     return NULL;
 
   /* Run the scheduler */
@@ -698,5 +741,11 @@ static void *silc_fsm_thread(void *context)
 
   fsm->schedule = old;
 
+  /* Finish the FSM thread in the main thread */
+  SILC_ASSERT(fsm->finished);
+  silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_finish_fsm,
+                                fsm, 0, 0);
+  silc_schedule_wakeup(fsm->schedule);
+
   return NULL;
 }