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;
}
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);
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)
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);
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);
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);
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;
SILC_FSM_STATE(test_st_fourth)
{
Foo f = fsm_context;
+ SilcFSMThread t;
SILC_LOG_DEBUG(("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;