Added silc_likely and silc_unlikely GCC branch prediction macros.
[silc.git] / lib / silcutil / silcfsm.c
index dcefb09cef98b208d93f6896a094c3fd57b5945c..4feffdffba0f74110f3d13f929278dcf49b8336e 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 Pekka Riikonen
+  Copyright (C) 2005 - 2006 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
@@ -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)
-  assert(!fsm->thread);
-#endif /* SILC_DEBUG */
+  SILC_ASSERT(!fsm->thread);
 
   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;
 
@@ -137,9 +137,12 @@ SILC_TASK_CALLBACK(silc_fsm_free_final)
   SilcFSM f = context;
 
 #if defined(SILC_DEBUG)
+  /* We must be finished */
+  SILC_ASSERT(f->finished);
+
   /* Machine must not have active threads */
   if (!f->thread && f->u.m.threads)
-    assert(f->u.m.threads == 0);
+    SILC_ASSERT(f->u.m.threads == 0);
 #endif /* SILC_DEBUG */
 
   if (!f->thread && f->u.m.lock)
@@ -156,36 +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);
-}
-
-/* FSM is uninitialized through scheduler to make sure that all dying
-   real system threads will have their finish callbacks scheduled before
-   this one (when SILC_FSM_THREAD_WAIT was used). */
-
-SILC_TASK_CALLBACK(silc_fsm_uninit_final)
-{
-  SilcFSM f = context;
-
-#if defined(SILC_DEBUG)
-  /* Machine must not have active threads */
-  if (!f->thread && f->u.m.threads)
-    assert(f->u.m.threads == 0);
-#endif /* SILC_DEBUG */
-
-  if (!f->thread && f->u.m.lock)
-    silc_mutex_free(f->u.m.lock);
-
-  if (f->thread && f->u.t.sema)
-    silc_fsm_sema_free(f->u.t.sema);
-}
-
-/* Uninitializes FSM */
-
-void silc_fsm_uninit(void *fsm)
-{
-  SilcFSM f = fsm;
-  silc_schedule_task_add_timeout(f->schedule, silc_fsm_uninit_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
@@ -218,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 */
@@ -241,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) {
@@ -273,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 */
@@ -280,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 */
@@ -288,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)
@@ -303,10 +336,18 @@ SilcSchedule silc_fsm_get_schedule(void *fsm)
 
 SilcFSM silc_fsm_get_machine(SilcFSMThread thread)
 {
-  assert(thread->thread);
+  SILC_ASSERT(thread->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)
@@ -344,12 +385,14 @@ void *silc_fsm_get_state_context(void *fsm)
 SilcBool silc_fsm_thread_wait(void *fsm, void *thread)
 {
   SilcFSM t = thread;
-#if defined(SILC_DEBUG)
-  assert(t->thread);
-#endif /* SILC_DEBUG */
+
+  SILC_ASSERT(t->thread);
+
   t->u.t.sema = silc_fsm_sema_alloc(t->u.t.fsm, 0);
   if (!t->u.t.sema)
     return FALSE;
+
+  SILC_LOG_DEBUG(("Waiting for thread %p to terminate", thread));
   silc_fsm_sema_wait(t->u.t.sema, fsm);
   return TRUE;
 }
@@ -369,6 +412,11 @@ SILC_TASK_CALLBACK(silc_fsm_run)
   while (status == SILC_FSM_CONTINUE);
 
   switch (status) {
+  case SILC_FSM_YIELD:
+    /* Continue through scheduler */
+    silc_fsm_continue(fsm);
+    break;
+
   case SILC_FSM_WAIT:
     /* The machine is in hold */
     SILC_LOG_DEBUG(("State wait %p", fsm));
@@ -378,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)
-    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_task_add_timeout(app_context, silc_fsm_finish, fsm, 0, 1);
-      silc_schedule_wakeup(app_context);
-      silc_schedule_stop(fsm->schedule);
-      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:
@@ -408,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;
 
@@ -433,6 +462,11 @@ SILC_TASK_CALLBACK(silc_fsm_finish)
       fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
 
   } else {
+    if (fsm->u.m.lock) {
+      silc_mutex_free(fsm->u.m.lock);
+      fsm->u.m.lock = NULL;
+    }
+
     /* Call the destructor callback. */
     if (fsm->destructor)
       fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
@@ -446,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);
@@ -460,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)
-  assert(!fsm->thread);
-#endif /* SILC_DEBUG */
+  SILC_ASSERT(!fsm->thread);
   memset(sema, 0, sizeof(*sema));
   sema->fsm = fsm;
   sema->refcnt = 0;
@@ -476,9 +508,8 @@ void silc_fsm_sema_free(SilcFSMSema sema)
 {
   if (sema->refcnt > 0)
     return;
-#if defined(SILC_DEBUG)
-  assert(silc_list_count(sema->waiters) == 0);
-#endif /* SILC_DEBUG */
+  if (silc_list_count(sema->waiters) > 0)
+    return;
   silc_free(sema);
 }
 
@@ -511,7 +542,7 @@ SilcUInt32 silc_fsm_sema_wait(SilcFSMSema sema, void *fsm)
     SilcFSM entry;
     silc_list_start(sema->waiters);
     while ((entry = silc_list_get(sema->waiters)) != SILC_LIST_END)
-      assert(entry != fsm);
+      SILC_ASSERT(entry != fsm);
 #endif /* SILC_DEBUG */
 
     SILC_LOG_DEBUG(("Waiting for semaphore %p", sema));
@@ -642,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;
@@ -650,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);
   }
 
@@ -665,7 +696,7 @@ static void silc_fsm_thread_termination_post(SilcFSMSema sema)
   SilcFSM fsm;
   SilcMutex lock = sema->fsm->u.m.lock;
 
-  SILC_LOG_DEBUG(("Post thread termination semaphore %p", sema));
+  SILC_LOG_DEBUG(("Post thread terminate semaphore %p", sema));
 
   silc_mutex_lock(lock);
 
@@ -694,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 */
@@ -709,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;
 }