PROBLEM REPORT ============== Situation --------- While developing a driver for RTEMS we've discovered a problem that causes the event mechanism to fail under some circumstances. The problem appears in RTEMS-4.0.0 and probably in RTEMS-4.5.0 (as the functions look identical at the first glance). Description ----------- The problem occurs when two different ISRs are sending different event sets in short intervals to a thread just calling rtems_event_receive. The problem is distributed over the rtems_event_receive and rtems_event_send primitives. If more than one ISR is invoked during the synchronization phase (where interrupts are enabled for a short time), only the last posted event set is received by the application because the rtems_event_send primitive always overwrites the return argument of the rtems_event_receive primitive. Thus, the thread is unblocked but the event set only returns the events posted by the last ISR. Possible fixes -------------- * rtems_event_receive could initialize the return argument to zero while the rtems_event_send function "ors" the seized events to the return argument. I'm not yet completely sure that this approach doesn't have side-effects (especially with the RTEMS_EVENT_ALL option). How to repeat ------------- See code example in appendix Remarks ------- There seems to be another problem but I couldn't setup an example that is affected: If two ISRs execute before _Thread_Set_state() is invoked by the thread, the first ISR satisfies the wait condition and the return value contains the posted events. The second ISR (now the sync state is SATISFIED) adds its events to the pending mask, but they are not added to the return argument, so their reception is delayed until the next call of rtems_event_receive. APPENDIX ======== Source code (event.c) with annotations -------------------------------------- _Event_Seize: ... _Event_Sync_state = EVENT_SYNC_NOTHING_HAPPENED; executing->Wait.option = (unsigned32) option_set; executing->Wait.count = (unsigned32) event_in; executing->Wait.return_argument = event_out; _ISR_Enable( level ); /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * First ISR executes rtems_send_event(thread, 0x00000001) here * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ if ( ticks ) { _Watchdog_Initialize( &executing->Timer, _Event_Timeout, executing->Object.id, NULL ); _Watchdog_Insert_ticks( &executing->Timer, ticks ); } _Thread_Set_state( executing, STATES_WAITING_FOR_EVENT ); /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * Second ISR executes rtems_send_event(thread, 0x00000002) here * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ _ISR_Disable( level ); sync_state = _Event_Sync_state; _Event_Sync_state = EVENT_SYNC_SYNCHRONIZED; ... _Event_Surrender: { ISR_Level level; rtems_event_set pending_events; rtems_event_set event_condition; rtems_event_set seized_events; rtems_option option_set; RTEMS_API_Control *api; api = the_thread->API_Extensions[ THREAD_API_RTEMS ]; option_set = (rtems_option) the_thread->Wait.option; _ISR_Disable( level ); pending_events = api->pending_events; event_condition = (rtems_event_set) the_thread->Wait.count; seized_events = _Event_sets_Get( pending_events, event_condition ); if ( !_Event_sets_Is_empty( seized_events ) ) { if ( _States_Is_waiting_for_event( the_thread->current_state ) ) { if ( seized_events == event_condition || _Options_Is_any( option_set ) ) { /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * The second ISR finds the thread in the waiting state and executes * this branch. It overwrites the already written return_argument so that the * events of the first ISR are never reported to the thread. * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ api->pending_events = _Event_sets_Clear( pending_events, seized_events ); *(rtems_event_set *)the_thread->Wait.return_argument = seized_events; _ISR_Flash( level ); if ( !_Watchdog_Is_active( &the_thread->Timer ) ) { _ISR_Enable( level ); _Thread_Unblock( the_thread ); } else { _Watchdog_Deactivate( &the_thread->Timer ); _ISR_Enable( level ); (void) _Watchdog_Remove( &the_thread->Timer ); _Thread_Unblock( the_thread ); } return; } } switch ( _Event_Sync_state ) { case EVENT_SYNC_SYNCHRONIZED: case EVENT_SYNC_SATISFIED: break; case EVENT_SYNC_NOTHING_HAPPENED: case EVENT_SYNC_TIMEOUT: if ( !_Thread_Is_executing( the_thread ) ) break; /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ /* The first ISR finds the thread not-waiting and executes this branch */ /* It sets the return_argument to the seized events. */ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ if ( seized_events == event_condition || _Options_Is_any(option_set) ) { api->pending_events = _Event_sets_Clear( pending_events,seized_events ); *(rtems_event_set *)the_thread->Wait.return_argument = seized_events; _Event_Sync_state = EVENT_SYNC_SATISFIED; } break; } } _ISR_Enable( level ); } How to repeat ------------- Required: A thread and more than one ISR Let n ISRs be triggered by high-speed repeating hardware timers with different periods. When the thread receives an RTEMS event, it sets a flag ("registered") and enables a led. When an ISR is invoked, the event is deregistered, the led disabled and an rtems_event is sent. If an event is lost, the led never is enabled again because the thread never reregisters the event. In fact, all but one led go out after some time when executing the program. Required Support functions: vec2nr(vec) converts a vector number to a timer number 0..n-1 led(nr, state) turns on/off the nr-th led #include #define HWTIMERS 4 volatile rtems_id task_id; volatile int isr_registered[HWTIMERS]; rtems_isr timer_isr(rtems_vector_number vec) { unsigned nr = vec2nr(vec); unsigned long cookie; _ISR_Disable(cookie); if(isr_registered[nr]) { isr_registered[nr]=0; led(nr, 0); (void) rtems_event_send(task_id, 1UL<