From 88e6f819b977bffc3d838988401cc89b5efef6b6 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Mon, 19 Feb 2007 14:40:02 +0000 Subject: [PATCH] Fixed event waiting/signalling when there are multiple signallers. --- lib/silcutil/silcfsm.c | 18 ++++++--- lib/silcutil/tests/test_silcfsm.c | 67 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/lib/silcutil/silcfsm.c b/lib/silcutil/silcfsm.c index aff9a77e..0098bf2f 100644 --- a/lib/silcutil/silcfsm.c +++ b/lib/silcutil/silcfsm.c @@ -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); diff --git a/lib/silcutil/tests/test_silcfsm.c b/lib/silcutil/tests/test_silcfsm.c index b0464393..f041630f 100644 --- a/lib/silcutil/tests/test_silcfsm.c +++ b/lib/silcutil/tests/test_silcfsm.c @@ -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; -- 2.24.0