forked from Imagelibrary/rtems
Ensure that the global construction is performed in the context of the first initialization thread. On SMP this was not guaranteed in the previous implementation.
407 lines
9.1 KiB
C
407 lines
9.1 KiB
C
/*
|
|
* 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 <rtems/score/threadimpl.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,
|
|
SET_PROTECTION,
|
|
SET_PROTECTION_DONE,
|
|
CLEAR_PROTECTION,
|
|
DELETE_4,
|
|
DELETE_5,
|
|
DELETE_6,
|
|
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);
|
|
|
|
switch (ctx->current) {
|
|
case RESTART_0:
|
|
rtems_test_assert(ctx->worker_task_id == rtems_task_self());
|
|
ctx->current = RESTART_1;
|
|
sc = rtems_task_restart(RTEMS_SELF, 0);
|
|
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
|
break;
|
|
case RESTART_1:
|
|
rtems_test_assert(ctx->worker_task_id == rtems_task_self());
|
|
ctx->current = RESTART_2;
|
|
break;
|
|
case INIT:
|
|
/* Restart via _Thread_Global_construction() */
|
|
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);
|
|
|
|
switch (ctx->current) {
|
|
case DELETE_2:
|
|
ctx->current = DELETE_3;
|
|
break;
|
|
case DELETE_5:
|
|
ctx->current = DELETE_6;
|
|
break;
|
|
default:
|
|
rtems_test_assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
case DELETE_4:
|
|
ctx->current = DELETE_5;
|
|
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;
|
|
bool previous_thread_life_protection;
|
|
|
|
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;
|
|
case SET_PROTECTION:
|
|
_Thread_Disable_dispatch();
|
|
previous_thread_life_protection = _Thread_Set_life_protection(true);
|
|
rtems_test_assert(!previous_thread_life_protection);
|
|
_Thread_Enable_dispatch();
|
|
break;
|
|
case CLEAR_PROTECTION:
|
|
_Thread_Disable_dispatch();
|
|
previous_thread_life_protection = _Thread_Set_life_protection(false);
|
|
rtems_test_assert(previous_thread_life_protection);
|
|
ctx->current = DELETE_4;
|
|
_Thread_Enable_dispatch();
|
|
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 create_and_start_worker(test_context *ctx)
|
|
{
|
|
rtems_status_code sc;
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
create_and_start_worker(ctx);
|
|
|
|
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));
|
|
|
|
create_and_start_worker(ctx);
|
|
|
|
change_state_and_wait(ctx, DELETE_3, SET_PROTECTION, SET_PROTECTION_DONE);
|
|
change_state(ctx, SET_PROTECTION_DONE, CLEAR_PROTECTION, INVALID);
|
|
|
|
sc = rtems_task_delete(ctx->worker_task_id);
|
|
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
|
|
|
rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
|
|
|
|
rtems_test_assert(ctx->current == DELETE_6);
|
|
}
|
|
|
|
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>
|