Fixed event waiting/signalling when there are multiple signallers.
authorPekka Riikonen <priikone@silcnet.org>
Mon, 19 Feb 2007 14:40:02 +0000 (14:40 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Mon, 19 Feb 2007 14:40:02 +0000 (14:40 +0000)
lib/silcutil/silcfsm.c
lib/silcutil/tests/test_silcfsm.c

index aff9a77edf5e1cf8d7c053083289e06fcf706475..0098bf2f551ade169311ca8b438769be5e09719c 100644 (file)
@@ -560,9 +560,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 +638,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 +652,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 +691,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);
index b0464393ac85551749286dfa9c8a6e1b92c22007..f041630f1cfe16426ef353723e9fb800f0a07481 100644 (file)
@@ -23,12 +23,15 @@ struct FooStruct {
   SilcFSMThreadStruct thread;
   int timeout;
   SilcFSMEventStruct sema;
+  SilcFSMEventStruct wait2;
   SilcSchedule schedule;
   Callback cb;
   void *cb_context;
   T threads[NUM_THREADS];
   T threads2[NUM_THREADS];
   int c;
+  int got_wait1 : 1;
+  int got_wait2 : 1;
 };
 
 SILC_FSM_STATE(test_st_start);
@@ -44,6 +47,11 @@ SILC_FSM_STATE(test_st_ninth);
 SILC_FSM_STATE(test_st_tenth);
 SILC_FSM_STATE(test_st_finish);
 
+SILC_FSM_STATE(test_st_wait1);
+SILC_FSM_STATE(test_st_wait2);
+SILC_FSM_STATE(test_st_signal1);
+SILC_FSM_STATE(test_st_signal1_check);
+
 SILC_FSM_STATE(test_thread_st_start);
 SILC_FSM_STATE(test_thread_st_finish);
 SILC_FSM_STATE(test_thread2_st_start);
@@ -51,6 +59,12 @@ SILC_FSM_STATE(test_thread2_st_finish);
 SILC_FSM_STATE(test_thread3_st_start);
 SILC_FSM_STATE(test_thread4_st_start);
 
+static void test_fsm_destr(SilcFSMThread thread, void *thread_context,
+                          void *user_context)
+{
+  silc_fsm_free(thread);
+}
+
 SILC_TASK_CALLBACK(async_call_timeout)
 {
   Foo f = context;
@@ -141,6 +155,7 @@ SILC_FSM_STATE(test_st_third)
 SILC_FSM_STATE(test_st_fourth)
 {
   Foo f = fsm_context;
+  SilcFSMThread t;
 
   SILC_LOG_DEBUG(("test_st_fourth"));
 
@@ -152,12 +167,64 @@ SILC_FSM_STATE(test_st_fourth)
   /*** Start thread */
   silc_fsm_start(&f->thread, test_thread_st_start);
 
+  SILC_LOG_DEBUG(("Creating two waiting threads"));
+  silc_fsm_event_init(&f->wait2, fsm);
+  t = silc_fsm_thread_alloc(fsm, f, test_fsm_destr, NULL, FALSE);
+  silc_fsm_start(t, test_st_wait1);
+  t = silc_fsm_thread_alloc(fsm, f, test_fsm_destr, NULL, FALSE);
+  silc_fsm_start(t, test_st_wait2);
+
+  SILC_LOG_DEBUG(("Create signaller thread"));
+  t = silc_fsm_thread_alloc(fsm, f, test_fsm_destr, NULL, FALSE);
+  silc_fsm_start(t, test_st_signal1);
+
   /** Waiting thread to terminate */
   SILC_LOG_DEBUG(("Waiting for thread to terminate"));
   silc_fsm_next(fsm, test_st_fifth);
   SILC_FSM_THREAD_WAIT(&f->thread);
 }
 
+SILC_FSM_STATE(test_st_wait1)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("Waiter 1"));
+  SILC_FSM_EVENT_WAIT(&f->wait2);
+  SILC_LOG_DEBUG(("Waiter 1 signalled"));
+  f->got_wait1 = 1;
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(test_st_wait2)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("Waiter 2"));
+  SILC_FSM_EVENT_WAIT(&f->wait2);
+  SILC_LOG_DEBUG(("Waiter 2 signalled"));
+  f->got_wait2 = 1;
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(test_st_signal1)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("Signaller 1"));
+  SILC_FSM_EVENT_SIGNAL(&f->wait2);
+  silc_fsm_next_later(fsm, test_st_signal1_check, 0, 500000); 
+  return SILC_FSM_WAIT;;
+}
+
+SILC_FSM_STATE(test_st_signal1_check)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("Signal check"));
+  assert(f->got_wait1 && f->got_wait2);
+  return SILC_FSM_FINISH;
+}
+
 SILC_FSM_STATE(test_thread_st_start)
 {
   Foo f = fsm_context;