From 39691cd1b158c8f3e23805a47f4c739e3276bd59 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 2 Jan 2013 18:56:35 +0100 Subject: rtems: Critical fix for events Commit 4b45c1393ce4ee3e1c2762ef3145d2bd6b5b38da 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. --- cpukit/rtems/src/eventtimeout.c | 19 +- testsuites/sptests/spintrcritical10/init.c | 308 +++++++++++++++++++-- .../sptests/spintrcritical10/spintrcritical10.scn | 5 +- 3 files changed, 305 insertions(+), 27 deletions(-) diff --git a/cpukit/rtems/src/eventtimeout.c b/cpukit/rtems/src/eventtimeout.c index c1eef71f9e..3c0b2bddcd 100644 --- a/cpukit/rtems/src/eventtimeout.c +++ b/cpukit/rtems/src/eventtimeout.c @@ -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 ) ) { diff --git a/testsuites/sptests/spintrcritical10/init.c b/testsuites/sptests/spintrcritical10/init.c index edbf61003e..05b8ee1744 100644 --- a/testsuites/sptests/spintrcritical10/init.c +++ b/testsuites/sptests/spintrcritical10/init.c @@ -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,37 +11,309 @@ * $Id$ */ +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + #define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 #include #include -rtems_id Main_task; +#define GREEN RTEMS_EVENT_0 -rtems_task Init( - rtems_task_argument ignored -) +#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; - rtems_event_set out; - int resets; + rtems_status_code sc; + test_context *ctx = arg; + const Thread_Control *thread = ctx->thread; - puts( "\n\n*** TEST INTERRUPT CRITICAL SECTION 10 ***" ); + 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); - 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_event_send(thread->Object.id, EVENTS); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); - Main_task = rtems_task_self(); + 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); - interrupt_critical_section_test_support_initialize( NULL ); + 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" + ); - for (resets=0 ; resets< 2 ;) { - if ( interrupt_critical_section_test_support_delay() ) + 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++; - sc = rtems_event_receive( 0x01, RTEMS_DEFAULT_OPTIONS, 1, &out ); - fatal_directive_status( sc, RTEMS_TIMEOUT, "event_receive timeout" ); + 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; + test_context ctx = { + .thread = _Thread_Executing + }; + + puts( "\n\n*** TEST INTERRUPT CRITICAL SECTION 10 ***" ); + + sc = rtems_timer_create(rtems_build_name('T', 'I', 'M', 'R'), &ctx.timer); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + 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 - -/* global variables */ diff --git a/testsuites/sptests/spintrcritical10/spintrcritical10.scn b/testsuites/sptests/spintrcritical10/spintrcritical10.scn index 9f59c0ea4d..e8db0c576a 100644 --- a/testsuites/sptests/spintrcritical10/spintrcritical10.scn +++ b/testsuites/sptests/spintrcritical10/spintrcritical10.scn @@ -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 *** -- cgit v1.2.3