mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-09-22 11:21:00 +08:00
score: Do not reset resource count during restart
This fixes an integer underflow problem in case resources are released after a thread restart. Add new test sptests/spthreadlife01.
This commit is contained in:
@@ -212,7 +212,6 @@ static void _Thread_Start_life_change(
|
||||
Priority_Control priority
|
||||
)
|
||||
{
|
||||
the_thread->resource_count = 0;
|
||||
the_thread->is_preemptible = the_thread->Start.is_preemptible;
|
||||
the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
|
||||
the_thread->budget_callout = the_thread->Start.budget_callout;
|
||||
|
||||
@@ -33,6 +33,7 @@ SUBDIRS = \
|
||||
spsignal_err01 spport_err01 spmsgq_err01 spmsgq_err02 spsem_err01 \
|
||||
spsem_err02 sptask_err01 spevent_err03 sptask_err03 sptask_err02 \
|
||||
sptask_err04 spclock_err01
|
||||
SUBDIRS += spthreadlife01
|
||||
SUBDIRS += spprofiling01
|
||||
SUBDIRS += spcache01
|
||||
SUBDIRS += sptls03
|
||||
|
||||
@@ -36,6 +36,7 @@ AM_CONDITIONAL(HAS_CPUSET,test x"${ac_cv_header_sys_cpuset_h}" = x"yes")
|
||||
|
||||
# Explicitly list all Makefiles here
|
||||
AC_CONFIG_FILES([Makefile
|
||||
spthreadlife01/Makefile
|
||||
spprofiling01/Makefile
|
||||
spcache01/Makefile
|
||||
sptls03/Makefile
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
rtems_tests_PROGRAMS = spthreadlife01
|
||||
spthreadlife01_SOURCES = init.c
|
||||
|
||||
dist_rtems_tests_DATA = spthreadlife01.scn spthreadlife01.doc
|
||||
|
||||
include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
|
||||
include $(top_srcdir)/../automake/compile.am
|
||||
include $(top_srcdir)/../automake/leaf.am
|
||||
|
||||
AM_CPPFLAGS += -I$(top_srcdir)/../support/include
|
||||
|
||||
LINK_OBJS = $(spthreadlife01_OBJECTS)
|
||||
LINK_LIBS = $(spthreadlife01_LDLIBS)
|
||||
|
||||
spthreadlife01$(EXEEXT): $(spthreadlife01_OBJECTS) $(spthreadlife01_DEPENDENCIES)
|
||||
@rm -f spthreadlife01$(EXEEXT)
|
||||
$(make-exe)
|
||||
|
||||
include $(top_srcdir)/../automake/local.am
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (c) 2014 embedded brains GmbH. All rights reserved.
|
||||
*
|
||||
* embedded brains GmbH
|
||||
* Dornierstr. 4
|
||||
* 82178 Puchheim
|
||||
* Germany
|
||||
* <rtems@embedded-brains.de>
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/libcsupport.h>
|
||||
|
||||
#include "tmacros.h"
|
||||
|
||||
#define PRIO_INIT 1
|
||||
#define PRIO_HIGH 2
|
||||
#define PRIO_MID 3
|
||||
#define PRIO_LOW 4
|
||||
|
||||
const char rtems_test_name[] = "SPTHREADLIFE 1";
|
||||
|
||||
typedef enum {
|
||||
INIT,
|
||||
SET_PRIO,
|
||||
SET_PRIO_DONE,
|
||||
DO_OBTAIN_0,
|
||||
OBTAIN_DONE_0,
|
||||
DO_RELEASE_0,
|
||||
RELEASE_DONE_0,
|
||||
DO_OBTAIN_1,
|
||||
OBTAIN_DONE_1,
|
||||
RESTART_0,
|
||||
RESTART_1,
|
||||
RESTART_2,
|
||||
RESTART_3,
|
||||
DO_RELEASE_1,
|
||||
RELEASE_DONE_1,
|
||||
DELETE_0,
|
||||
DELETE_1,
|
||||
DELETE_2,
|
||||
DELETE_3,
|
||||
INVALID
|
||||
} test_state;
|
||||
|
||||
typedef struct {
|
||||
rtems_id main_task_id;
|
||||
rtems_id worker_task_id;
|
||||
rtems_id sema_id;
|
||||
test_state current;
|
||||
test_state next;
|
||||
} test_context;
|
||||
|
||||
static test_context test_instance;
|
||||
|
||||
static void wake_up_main(const test_context *ctx)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
|
||||
sc = rtems_event_transient_send(ctx->main_task_id);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
}
|
||||
|
||||
static void wait(void)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
|
||||
sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
}
|
||||
|
||||
static void change_state(
|
||||
test_context *ctx,
|
||||
test_state expected,
|
||||
test_state current,
|
||||
test_state next
|
||||
)
|
||||
{
|
||||
rtems_test_assert(ctx->current == expected);
|
||||
ctx->current = current;
|
||||
ctx->next = next;
|
||||
}
|
||||
|
||||
static void change_state_and_wait(
|
||||
test_context *ctx,
|
||||
test_state expected,
|
||||
test_state current,
|
||||
test_state next
|
||||
)
|
||||
{
|
||||
change_state(ctx, expected, current, next);
|
||||
wait();
|
||||
}
|
||||
|
||||
static void set_priority(rtems_task_priority prio)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
|
||||
sc = rtems_task_set_priority(RTEMS_SELF, prio, &prio);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
}
|
||||
|
||||
static void assert_priority(rtems_task_priority expected)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
rtems_task_priority prio;
|
||||
|
||||
sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &prio);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
rtems_test_assert(prio == expected);
|
||||
}
|
||||
|
||||
static void restart_extension(
|
||||
Thread_Control *executing,
|
||||
Thread_Control *restarted
|
||||
)
|
||||
{
|
||||
test_context *ctx = &test_instance;
|
||||
rtems_status_code sc;
|
||||
|
||||
rtems_test_assert(executing == restarted);
|
||||
rtems_test_assert(ctx->worker_task_id == rtems_task_self());
|
||||
|
||||
switch (ctx->current) {
|
||||
case RESTART_0:
|
||||
ctx->current = RESTART_1;
|
||||
sc = rtems_task_restart(RTEMS_SELF, 0);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
break;
|
||||
case RESTART_1:
|
||||
ctx->current = RESTART_2;
|
||||
break;
|
||||
default:
|
||||
rtems_test_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_extension(
|
||||
Thread_Control *executing,
|
||||
Thread_Control *deleted
|
||||
)
|
||||
{
|
||||
test_context *ctx = &test_instance;
|
||||
|
||||
rtems_test_assert(executing != deleted);
|
||||
rtems_test_assert(ctx->main_task_id == rtems_task_self());
|
||||
|
||||
assert_priority(PRIO_INIT);
|
||||
|
||||
rtems_test_assert(ctx->current == DELETE_2);
|
||||
ctx->current = DELETE_3;
|
||||
}
|
||||
|
||||
static void terminate_extension(Thread_Control *executing)
|
||||
{
|
||||
test_context *ctx = &test_instance;
|
||||
rtems_status_code sc;
|
||||
|
||||
rtems_test_assert(ctx->worker_task_id == rtems_task_self());
|
||||
|
||||
assert_priority(PRIO_INIT);
|
||||
|
||||
switch (ctx->current) {
|
||||
case DELETE_0:
|
||||
ctx->current = DELETE_1;
|
||||
sc = rtems_task_delete(RTEMS_SELF);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
break;
|
||||
case DELETE_1:
|
||||
ctx->current = DELETE_2;
|
||||
break;
|
||||
default:
|
||||
rtems_test_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void worker_task(rtems_task_argument arg)
|
||||
{
|
||||
test_context *ctx = &test_instance;
|
||||
|
||||
while (true) {
|
||||
test_state state = ctx->current;
|
||||
rtems_status_code sc;
|
||||
|
||||
switch (state) {
|
||||
case SET_PRIO:
|
||||
assert_priority(PRIO_LOW);
|
||||
set_priority(PRIO_MID);
|
||||
break;
|
||||
case DO_OBTAIN_0:
|
||||
case DO_OBTAIN_1:
|
||||
assert_priority(PRIO_MID);
|
||||
sc = rtems_semaphore_obtain(
|
||||
ctx->sema_id,
|
||||
RTEMS_WAIT,
|
||||
RTEMS_NO_TIMEOUT
|
||||
);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
assert_priority(PRIO_HIGH);
|
||||
break;
|
||||
case DO_RELEASE_0:
|
||||
case DO_RELEASE_1:
|
||||
assert_priority(PRIO_HIGH);
|
||||
sc = rtems_semaphore_release(ctx->sema_id);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
switch (state) {
|
||||
case DO_RELEASE_0:
|
||||
assert_priority(PRIO_MID);
|
||||
break;
|
||||
case DO_RELEASE_1:
|
||||
assert_priority(PRIO_LOW);
|
||||
break;
|
||||
default:
|
||||
rtems_test_assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case RESTART_2:
|
||||
assert_priority(PRIO_HIGH);
|
||||
break;
|
||||
default:
|
||||
rtems_test_assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->current = ctx->next;
|
||||
wake_up_main(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_sema(test_context *ctx)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
|
||||
sc = rtems_semaphore_create(
|
||||
rtems_build_name('S', 'E', 'M', 'A'),
|
||||
1,
|
||||
RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_PRIORITY_CEILING,
|
||||
PRIO_HIGH,
|
||||
&ctx->sema_id
|
||||
);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
}
|
||||
|
||||
static void test(void)
|
||||
{
|
||||
test_context *ctx = &test_instance;
|
||||
rtems_status_code sc;
|
||||
rtems_resource_snapshot snapshot;
|
||||
|
||||
ctx->main_task_id = rtems_task_self();
|
||||
|
||||
rtems_resource_snapshot_take(&snapshot);
|
||||
|
||||
create_sema(ctx);
|
||||
|
||||
sc = rtems_task_create(
|
||||
rtems_build_name('W', 'O', 'R', 'K'),
|
||||
PRIO_LOW,
|
||||
RTEMS_MINIMUM_STACK_SIZE,
|
||||
RTEMS_DEFAULT_MODES,
|
||||
RTEMS_DEFAULT_ATTRIBUTES,
|
||||
&ctx->worker_task_id
|
||||
);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
sc = rtems_task_start(ctx->worker_task_id, worker_task, 0);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
change_state_and_wait(ctx, INIT, SET_PRIO, SET_PRIO_DONE);
|
||||
change_state_and_wait(ctx, SET_PRIO_DONE, DO_OBTAIN_0, OBTAIN_DONE_0);
|
||||
|
||||
sc = rtems_semaphore_delete(ctx->sema_id);
|
||||
rtems_test_assert(sc == RTEMS_RESOURCE_IN_USE);
|
||||
|
||||
change_state_and_wait(ctx, OBTAIN_DONE_0, DO_RELEASE_0, RELEASE_DONE_0);
|
||||
|
||||
sc = rtems_semaphore_delete(ctx->sema_id);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
create_sema(ctx);
|
||||
|
||||
change_state_and_wait(ctx, RELEASE_DONE_0, DO_OBTAIN_1, OBTAIN_DONE_1);
|
||||
|
||||
sc = rtems_semaphore_delete(ctx->sema_id);
|
||||
rtems_test_assert(sc == RTEMS_RESOURCE_IN_USE);
|
||||
|
||||
sc = rtems_task_restart(ctx->worker_task_id, 0);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
change_state_and_wait(ctx, OBTAIN_DONE_1, RESTART_0, RESTART_3);
|
||||
change_state_and_wait(ctx, RESTART_3, DO_RELEASE_1, RELEASE_DONE_1);
|
||||
|
||||
sc = rtems_semaphore_delete(ctx->sema_id);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
change_state(ctx, RELEASE_DONE_1, DELETE_0, INVALID);
|
||||
|
||||
sc = rtems_task_delete(ctx->worker_task_id);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
|
||||
rtems_test_assert(ctx->current == DELETE_2);
|
||||
|
||||
rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
|
||||
|
||||
rtems_test_assert(ctx->current == DELETE_3);
|
||||
}
|
||||
|
||||
static void Init(rtems_task_argument arg)
|
||||
{
|
||||
TEST_BEGIN();
|
||||
|
||||
test();
|
||||
|
||||
TEST_END();
|
||||
rtems_test_exit(0);
|
||||
}
|
||||
|
||||
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
||||
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
|
||||
|
||||
#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
|
||||
|
||||
#define CONFIGURE_MAXIMUM_TASKS 2
|
||||
#define CONFIGURE_MAXIMUM_SEMAPHORES 1
|
||||
|
||||
#define CONFIGURE_INITIAL_EXTENSIONS \
|
||||
{ \
|
||||
.thread_restart = restart_extension, \
|
||||
.thread_delete = delete_extension, \
|
||||
.thread_terminate = terminate_extension \
|
||||
}, \
|
||||
RTEMS_TEST_INITIAL_EXTENSION
|
||||
|
||||
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
||||
|
||||
#define CONFIGURE_INIT
|
||||
|
||||
#include <rtems/confdefs.h>
|
||||
@@ -0,0 +1,14 @@
|
||||
This file describes the directives and concepts tested by this test set.
|
||||
|
||||
test set name: spthreadlife01
|
||||
|
||||
directives:
|
||||
|
||||
- rtems_task_restart()
|
||||
- rtems_task_delete()
|
||||
|
||||
concepts:
|
||||
|
||||
- Ensure that semaphores owned by a restarted thread can be deleted.
|
||||
- Ensure that the task priority is adjusted properly across task restarts.
|
||||
- Ensure that the task priority is adjusted properly across task deletes.
|
||||
@@ -0,0 +1,2 @@
|
||||
*** BEGIN OF TEST SPTHREADLIFE 1 ***
|
||||
*** END OF TEST SPTHREADLIFE 1 ***
|
||||
Reference in New Issue
Block a user