rtems: Critical fix for events

Commit 4b45c1393c marked a test in
_Event_Timeout() as debug only.  This test is required also in non-debug
configurations since otherwise state corruption can happen.  A revised
test sptests/spintrcritical10 checks the relevant sequences.
This commit is contained in:
Sebastian Huber
2013-01-02 18:56:35 +01:00
parent 073e3e106b
commit 39691cd1b1
3 changed files with 307 additions and 29 deletions

View File

@@ -64,13 +64,18 @@ void _Event_Timeout(
* a timeout is not allowed to occur.
*/
_ISR_Disable( level );
#if defined(RTEMS_DEBUG)
if ( !the_thread->Wait.count ) { /* verify thread is waiting */
_Thread_Unnest_dispatch();
_ISR_Enable( level );
return;
}
#endif
/*
* Verify that the thread is still waiting for the event condition.
* This test is necessary to avoid state corruption if the timeout
* happens after the event condition is satisfied in
* _Event_Surrender(). A satisfied event condition is indicated with
* count set to zero.
*/
if ( !the_thread->Wait.count ) {
_Thread_Unnest_dispatch();
_ISR_Enable( level );
return;
}
the_thread->Wait.count = 0;
if ( _Thread_Is_executing( the_thread ) ) {

View File

@@ -2,6 +2,8 @@
* COPYRIGHT (c) 1989-2009.
* On-Line Applications Research Corporation (OAR).
*
* Copyright (c) 2013 embedded brains GmbH.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
@@ -9,36 +11,308 @@
* $Id$
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1
#include <tmacros.h>
#include <intrcritical.h>
rtems_id Main_task;
#define GREEN RTEMS_EVENT_0
rtems_task Init(
#define RED RTEMS_EVENT_1
#define EVENTS (GREEN | RED)
#define DEADBEEF 0xdeadbeef
typedef struct {
rtems_id timer;
Thread_Control *thread;
bool hit;
} test_context;
static void any_satisfy_before_timeout(rtems_id timer, void *arg)
{
rtems_status_code sc;
test_context *ctx = arg;
const Thread_Control *thread = ctx->thread;
if (thread->Wait.count != 0) {
ctx->hit = _Event_Sync_state == THREAD_BLOCKING_OPERATION_NOTHING_HAPPENED;
rtems_test_assert(thread->Wait.count == EVENTS);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == DEADBEEF
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
sc = rtems_event_send(thread->Object.id, GREEN);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(thread->Wait.count == 0);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == GREEN
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
sc = rtems_event_send(thread->Object.id, RED);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(thread->Wait.count == 0);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == GREEN
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
_Event_Timeout(thread->Object.id, &_Event_Sync_state);
rtems_test_assert(thread->Wait.count == 0);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == GREEN
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
if (ctx->hit) {
rtems_test_assert(
_Event_Sync_state == THREAD_BLOCKING_OPERATION_SATISFIED
);
}
}
sc = rtems_timer_reset(timer);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void test_any_satisfy_before_timeout(test_context *ctx)
{
rtems_status_code sc;
int resets = 0;
puts(
"Init - Trying to generate any satisfied before timeout "
"while blocking on event"
);
ctx->hit = false;
interrupt_critical_section_test_support_initialize(NULL);
sc = rtems_timer_fire_after(ctx->timer, 1, any_satisfy_before_timeout, ctx);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
while (!ctx->hit && resets < 2) {
rtems_event_set out;
if (interrupt_critical_section_test_support_delay())
resets++;
out = DEADBEEF;
sc = rtems_event_receive(EVENTS, RTEMS_EVENT_ANY | RTEMS_WAIT, 1, &out);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(out == GREEN);
out = DEADBEEF;
sc = rtems_event_receive(EVENTS, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, 0, &out);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(out == RED);
}
sc = rtems_timer_cancel(ctx->timer);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(ctx->hit);
}
static void all_satisfy_before_timeout(rtems_id timer, void *arg)
{
rtems_status_code sc;
test_context *ctx = arg;
const Thread_Control *thread = ctx->thread;
if (thread->Wait.count != 0) {
ctx->hit = _Event_Sync_state == THREAD_BLOCKING_OPERATION_NOTHING_HAPPENED;
rtems_test_assert(thread->Wait.count == EVENTS);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == DEADBEEF
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
sc = rtems_event_send(thread->Object.id, GREEN);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(thread->Wait.count == EVENTS);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == DEADBEEF
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
sc = rtems_event_send(thread->Object.id, RED);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(thread->Wait.count == 0);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == EVENTS
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
_Event_Timeout(thread->Object.id, &_Event_Sync_state);
rtems_test_assert(thread->Wait.count == 0);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == EVENTS
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
if (ctx->hit) {
rtems_test_assert(
_Event_Sync_state == THREAD_BLOCKING_OPERATION_SATISFIED
);
}
}
sc = rtems_timer_reset(timer);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void test_all_satisfy_before_timeout(test_context *ctx)
{
rtems_status_code sc;
int resets = 0;
puts(
"Init - Trying to generate all satisfied before timeout "
"while blocking on event"
);
ctx->hit = false;
interrupt_critical_section_test_support_initialize(NULL);
sc = rtems_timer_fire_after(ctx->timer, 1, all_satisfy_before_timeout, ctx);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
while (!ctx->hit && resets < 2) {
rtems_event_set out;
if (interrupt_critical_section_test_support_delay())
resets++;
out = DEADBEEF;
sc = rtems_event_receive(EVENTS, RTEMS_EVENT_ALL | RTEMS_WAIT, 1, &out);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(out == EVENTS);
}
sc = rtems_timer_cancel(ctx->timer);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(ctx->hit);
}
static void timeout_before_satisfied(rtems_id timer, void *arg)
{
rtems_status_code sc;
test_context *ctx = arg;
const Thread_Control *thread = ctx->thread;
if (thread->Wait.count != 0) {
ctx->hit =
_Event_Sync_state == THREAD_BLOCKING_OPERATION_NOTHING_HAPPENED;
rtems_test_assert(thread->Wait.count == EVENTS);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == DEADBEEF
);
rtems_test_assert(thread->Wait.return_code == RTEMS_SUCCESSFUL);
_Event_Timeout(thread->Object.id, &_Event_Sync_state);
rtems_test_assert(thread->Wait.count == 0);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == DEADBEEF
);
rtems_test_assert(thread->Wait.return_code == RTEMS_TIMEOUT);
sc = rtems_event_send(thread->Object.id, EVENTS);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(thread->Wait.count == 0);
rtems_test_assert(
*(rtems_event_set *) thread->Wait.return_argument == DEADBEEF
);
rtems_test_assert(thread->Wait.return_code == RTEMS_TIMEOUT);
if (ctx->hit) {
rtems_test_assert(
_Event_Sync_state == THREAD_BLOCKING_OPERATION_TIMEOUT
);
}
}
sc = rtems_timer_reset(timer);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void test_timeout_before_all_satisfy(test_context *ctx)
{
rtems_status_code sc;
int resets = 0;
puts(
"Init - Trying to generate timeout before all satisfied "
"while blocking on event"
);
ctx->hit = false;
interrupt_critical_section_test_support_initialize(NULL);
sc = rtems_timer_fire_after(ctx->timer, 1, timeout_before_satisfied, ctx);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
while (!ctx->hit && resets < 2) {
rtems_event_set out;
if (interrupt_critical_section_test_support_delay())
resets++;
out = DEADBEEF;
sc = rtems_event_receive(EVENTS, RTEMS_EVENT_ALL | RTEMS_WAIT, 1, &out);
rtems_test_assert(sc == RTEMS_TIMEOUT);
rtems_test_assert(out == DEADBEEF);
out = DEADBEEF;
sc = rtems_event_receive(EVENTS, RTEMS_EVENT_ALL | RTEMS_NO_WAIT, 0, &out);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(out == EVENTS);
}
sc = rtems_timer_cancel(ctx->timer);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_assert(ctx->hit);
}
static rtems_task Init(
rtems_task_argument ignored
)
{
rtems_status_code sc;
rtems_event_set out;
int resets;
rtems_status_code sc;
test_context ctx = {
.thread = _Thread_Executing
};
puts( "\n\n*** TEST INTERRUPT CRITICAL SECTION 10 ***" );
puts( "Init - Test may not be able to detect case is hit reliably" );
puts( "Init - Trying to generate timeout while blocking on event" );
sc = rtems_timer_create(rtems_build_name('T', 'I', 'M', 'R'), &ctx.timer);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
Main_task = rtems_task_self();
interrupt_critical_section_test_support_initialize( NULL );
for (resets=0 ; resets< 2 ;) {
if ( interrupt_critical_section_test_support_delay() )
resets++;
sc = rtems_event_receive( 0x01, RTEMS_DEFAULT_OPTIONS, 1, &out );
fatal_directive_status( sc, RTEMS_TIMEOUT, "event_receive timeout" );
}
test_any_satisfy_before_timeout(&ctx);
test_all_satisfy_before_timeout(&ctx);
test_timeout_before_all_satisfy(&ctx);
puts( "*** END OF TEST INTERRUPT CRITICAL SECTION 10 ***" );
rtems_test_exit(0);
@@ -56,5 +330,3 @@ rtems_task Init(
#define CONFIGURE_INIT
#include <rtems/confdefs.h>
/* global variables */

View File

@@ -1,4 +1,5 @@
*** TEST INTERRUPT CRITICAL SECTION 10 ***
Init - Test may not be able to detect case is hit reliably
Init - Trying to generate timeout while blocking on event
Init - Trying to generate any satisfied before timeout while blocking on event
Init - Trying to generate all satisfied before timeout while blocking on event
Init - Trying to generate timeout before all satisfied while blocking on event
*** END OF TEST INTERRUPT CRITICAL SECTION 10 ***