summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2021-08-13 09:52:04 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2021-08-16 19:29:41 +0200
commit8293cd477996ef7797a234e1fa2a0aa47ee6d3bd (patch)
treefec05e862a16e84d6fb6407658307cb52912ba1a
parentvalidation: Add ${.:skip} for action requirements (diff)
downloadrtems-central-8293cd477996ef7797a234e1fa2a0aa47ee6d3bd.tar.bz2
spec: Improve rtems_task_construct() validation
Call rtems_task_construct() with a zombie thread if it should be called with at least one inactive task available. Try to make the zombie thread executing in SMP configurations.
-rw-r--r--spec/rtems/task/req/construct.yml (renamed from spec/rtems/task/req/construct-errors.yml)345
1 files changed, 261 insertions, 84 deletions
diff --git a/spec/rtems/task/req/construct-errors.yml b/spec/rtems/task/req/construct.yml
index de595c14..3a4bfc28 100644
--- a/spec/rtems/task/req/construct-errors.yml
+++ b/spec/rtems/task/req/construct.yml
@@ -60,7 +60,7 @@ post-conditions:
id = 0;
sc = rtems_task_ident( NAME, RTEMS_SEARCH_LOCAL_NODE, &id );
T_rsc_success( sc );
- T_eq_u32( id, ctx->id_value );
+ T_eq_u32( id, ctx->id_obj );
text: |
The unique object name shall identify the task constructed by
the ${../if/construct:/name} call.
@@ -74,12 +74,12 @@ post-conditions:
test-prologue: |
rtems_status_code sc;
rtems_id id;
-- name: IdVar
+- name: IdObj
states:
- name: Set
test-code: |
- T_eq_ptr( ctx->id, &ctx->id_value );
- T_ne_u32( ctx->id_value, INVALID_ID );
+ T_eq_ptr( ctx->id, &ctx->id_obj );
+ T_ne_u32( ctx->id_obj, INVALID_ID );
text: |
The value of the object referenced by the
${../if/construct:/params[1]/name} parameter shall be set to the object
@@ -87,52 +87,77 @@ post-conditions:
${../if/construct:/name} call.
- name: Nop
test-code: |
- T_eq_u32( ctx->id_value, INVALID_ID );
+ T_eq_u32( ctx->id_obj, INVALID_ID );
text: |
Objects referenced by the ${../if/construct:/params[1]/name} parameter in
past calls to ${../if/construct:/name} shall not be accessed by the
${../if/construct:/name} call.
test-epilogue: null
test-prologue: null
-- name: CreateExt
+- name: CreateNew
states:
- name: All
test-code: |
T_eq_u32( ctx->create_extension_calls, 2 );
text: |
- The create user extensions shall be invoked during the
- ${../if/construct:/name} call.
+ The thread create user extensions shall be invoked for the task under
+ construction during the ${../if/construct:/name} call.
- name: UpToFailing
test-code: |
T_eq_u32( ctx->create_extension_calls, 1 );
text: |
- The create user extensions up to the failing extension shall be invoked
- during the ${../if/construct:/name} call.
- - name: 'No'
+ The thread create user extensions up to the failing extension shall be
+ invoked for the task under construction during the
+ ${../if/construct:/name} call.
+ - name: Nop
test-code: |
T_eq_u32( ctx->create_extension_calls, 0 );
text: |
- The create user extensions shall not be invoked during the
- ${../if/construct:/name} call.
+ The thread create user extensions shall not be invoked for the task under
+ construction during the ${../if/construct:/name} call.
test-epilogue: null
test-prologue: null
-- name: DelExt
+- name: DeleteNew
states:
- - name: 'Yes'
+ - name: All
test-code: |
T_eq_u32( ctx->delete_extension_calls, 2 );
text: |
- The delete user extensions shall be invoked during the
- ${../if/construct:/name} call.
- - name: 'No'
+ The thread delete user extensions shall be invoked for the task under
+ construction during the ${../if/construct:/name} call.
+ - name: Nop
test-code: |
T_eq_u32( ctx->delete_extension_calls, 0 );
text: |
- The delete user extensions shall not be invoked during the
+ The thread delete user extensions shall not be invoked for the task under
+ construction during the ${../if/construct:/name} call.
+ test-epilogue: null
+ test-prologue: null
+- name: KillZombies
+ states:
+ - name: 'Yes'
+ test-code: |
+ /*
+ * We cannot check the zombie delete extension calls if we should call
+ * rtems_task_construct() without an inactive TCB available. Killing
+ * a zombie would make one inactive TCB available.
+ */
+ if ( ctx->seized_objects == NULL ) {
+ T_eq_u32( ctx->delete_zombie_extension_calls, 2 );
+ }
+ text: |
+ The registered zombie threads shall be killed before an attempt to
+ allocate a ${/glossary/tcb:/term} is made by the ${../if/construct:/name}
+ call.
+ - name: 'No'
+ test-code: |
+ T_eq_u32( ctx->delete_zombie_extension_calls, 0 );
+ text: |
+ The registered zombie threads shall not be killed by the
${../if/construct:/name} call.
test-epilogue: null
test-prologue: null
-- name: StoFree
+- name: StorageFree
states:
- name: 'Yes'
test-code: |
@@ -153,7 +178,7 @@ pre-conditions:
states:
- name: Valid
test-code: |
- ctx->config = &ctx->config_value;
+ ctx->config = &ctx->config_obj;
text: |
While the ${../if/construct:/params[0]/name} parameter references an
object of type ${../if/config:/name}.
@@ -169,12 +194,12 @@ pre-conditions:
states:
- name: Valid
test-code: |
- ctx->config_value.name = NAME;
+ ctx->config_obj.name = NAME;
text: |
While the name of the task configuration is valid.
- - name: Inv
+ - name: Invalid
test-code: |
- ctx->config_value.name = 0;
+ ctx->config_obj.name = 0;
text: |
While the name of the task configuration is invalid.
test-epilogue: null
@@ -183,7 +208,7 @@ pre-conditions:
states:
- name: Valid
test-code: |
- ctx->id = &ctx->id_value;
+ ctx->id = &ctx->id_obj;
text: |
While the ${../if/construct:/params[1]/name} parameter references an
object of type ${../../type/if/id:/name}.
@@ -195,11 +220,11 @@ pre-conditions:
${/c/if/null:/name}.
test-epilogue: null
test-prologue: null
-- name: SysTsk
+- name: SystemTask
states:
- name: 'Yes'
test-code: |
- ctx->config_value.attributes |= RTEMS_SYSTEM_TASK;
+ ctx->config_obj.attributes |= RTEMS_SYSTEM_TASK;
text: |
While the attributes of the task configuration specifies a system task.
- name: 'No'
@@ -210,22 +235,22 @@ pre-conditions:
task.
test-epilogue: null
test-prologue: null
-- name: Prio
+- name: Priority
states:
- name: Valid
test-code: |
- ctx->config_value.initial_priority = 254;
+ ctx->config_obj.initial_priority = 254;
text: |
While the initial priority of the task configuration is valid and
non-zero.
- name: Zero
test-code: |
- ctx->config_value.initial_priority = 0;
+ ctx->config_obj.initial_priority = 0;
text: |
While the initial priority of the task configuration is zero.
- - name: Inv
+ - name: Invalid
test-code: |
- ctx->config_value.initial_priority = 0xffffffff;
+ ctx->config_obj.initial_priority = 0xffffffff;
text: |
While the initial priority of the task configuration is invalid.
test-epilogue: null
@@ -248,13 +273,13 @@ pre-conditions:
states:
- name: Enough
test-code: |
- ctx->config_value.maximum_thread_local_storage_size = MAX_TLS_SIZE;
+ ctx->config_obj.maximum_thread_local_storage_size = MAX_TLS_SIZE;
text: |
While the maximum thread-local storage size of the task configuration is
greater than or equal to the thread-local storage size.
- - name: Small
+ - name: TooSmall
test-code: |
- ctx->config_value.maximum_thread_local_storage_size = 0;
+ ctx->config_obj.maximum_thread_local_storage_size = 0;
text: |
While the maximum thread-local storage size of the task configuration is
less than the thread-local storage size.
@@ -268,7 +293,7 @@ pre-conditions:
text: |
While the task stack size of the task configuration is greater than or
equal to the configured minimum size.
- - name: Small
+ - name: TooSmall
test-code: |
ctx->stack_size = 0;
text: |
@@ -276,18 +301,18 @@ pre-conditions:
configured minimum size.
test-epilogue: null
test-prologue: null
-- name: Ext
+- name: Create
states:
- name: Ok
test-code: |
ctx->create_extension_status = true;
text: |
- While none of the task create extensions fails.
- - name: Err
+ While none of the thread create user extensions fails.
+ - name: Error
test-code: |
ctx->create_extension_status = false;
text: |
- While at least one of the task create extensions fails.
+ While at least one of the thread create user extensions fails.
test-epilogue: null
test-prologue: null
rationale: null
@@ -295,27 +320,48 @@ references: []
requirement-type: functional
skip-reasons: {}
test-action: |
+ if ( ctx->seized_objects == NULL ) {
+ PrepareZombie( ctx );
+ }
+
ctx->create_extension_calls = 0;
ctx->delete_extension_calls = 0;
+ ctx->delete_zombie_extension_calls = 0;
ctx->storage_free_calls = 0;
- ctx->config_value.storage_size = RTEMS_TASK_STORAGE_SIZE(
- ctx->config_value.maximum_thread_local_storage_size + ctx->stack_size,
- ctx->config_value.attributes
+ ctx->config_obj.storage_size = RTEMS_TASK_STORAGE_SIZE(
+ ctx->config_obj.maximum_thread_local_storage_size + ctx->stack_size,
+ ctx->config_obj.attributes
);
ctx->status = rtems_task_construct( ctx->config, ctx->id );
test-brief: null
test-cleanup: |
- if ( ctx->id_value != INVALID_ID ) {
+ if ( ctx->id_obj != INVALID_ID ) {
rtems_status_code sc;
- sc = rtems_task_delete( ctx->id_value );
+ sc = rtems_task_delete( ctx->id_obj );
T_rsc_success( sc );
- ctx->id_value = INVALID_ID;
+ ctx->id_obj = INVALID_ID;
}
T_surrender_objects( &ctx->seized_objects, rtems_task_delete );
test-context:
+- brief: |
+ This member contains the scheduler B identifier.
+ description: null
+ member: |
+ rtems_id scheduler_b_id
+- brief: |
+ This member contains the thread zombie registry ticket right before the
+ task exit of the zombie task.
+ description: null
+ member: |
+ unsigned int thread_zombie_ticket
+- brief: |
+ If this member is true, then the zombie thread is ready to get killed.
+ description: null
+ member: |
+ volatile bool zombie_ready;
- brief: null
description: null
member: |
@@ -327,7 +373,11 @@ test-context:
- brief: null
description: null
member: |
- rtems_task_config config_value
+ rtems_task_config config_obj
+- brief: null
+ description: null
+ member: |
+ rtems_id zombie_id
- brief: null
description: null
member: |
@@ -335,7 +385,7 @@ test-context:
- brief: null
description: null
member: |
- rtems_id id_value
+ rtems_id id_obj
- brief: null
description: null
member: |
@@ -351,6 +401,10 @@ test-context:
- brief: null
description: null
member: |
+ uint32_t delete_zombie_extension_calls
+- brief: null
+ description: null
+ member: |
uint32_t storage_free_calls
- brief: null
description: null
@@ -369,29 +423,40 @@ test-description: null
test-header: null
test-includes:
- rtems.h
-- string.h
-- rtems/score/apimutex.h
+- rtems/score/atomic.h
+- rtems/score/percpu.h
- rtems/score/threadimpl.h
+- string.h
test-local-includes:
+- ts-config.h
- tx-support.h
test-prepare: |
- _RTEMS_Lock_allocator();
- _Thread_Kill_zombies();
- _RTEMS_Unlock_allocator();
-
- ctx->id_value = INVALID_ID;
- memset( &ctx->config_value, 0, sizeof( ctx->config_value ) );
- ctx->config_value.storage_area = task_storage,
- ctx->config_value.storage_free = StorageFree;
+ KillZombies();
+ ctx->id_obj = INVALID_ID;
+ memset( &ctx->config_obj, 0, sizeof( ctx->config_obj ) );
+ ctx->config_obj.storage_area = task_storage,
+ ctx->config_obj.storage_free = StorageFree;
test-setup:
brief: null
code: |
rtems_status_code sc;
int var;
- var = tls_variable;
+ var = tls_object;
RTEMS_OBFUSCATE_VARIABLE( var );
- tls_variable = var;
+ tls_object = var;
+
+ ctx->scheduler_b_id = INVALID_ID;
+ #if defined(RTEMS_SMP)
+ ctx->zombie_ready = true;
+ if ( rtems_scheduler_get_processor_maximum() > 1 ) {
+ sc = rtems_scheduler_ident(
+ TEST_SCHEDULER_B_NAME,
+ &ctx->scheduler_b_id
+ );
+ T_rsc_success( sc );
+ }
+ #endif
sc = rtems_extension_create(
rtems_build_name( 'E', 'X', 'T', '1' ),
@@ -406,14 +471,16 @@ test-setup:
&ctx->extension_ids[ 1 ]
);
T_rsc_success( sc );
+
+ SetSelfPriority( PRIO_NORMAL );
description: null
test-stop: null
test-support: |
#define NAME rtems_build_name( 'T', 'E', 'S', 'T' )
- typedef RtemsTaskReqConstructErrors_Context Context;
+ typedef RtemsTaskReqConstruct_Context Context;
- static _Thread_local int tls_variable;
+ static volatile _Thread_local int tls_object;
#define MAX_TLS_SIZE RTEMS_ALIGN_UP( 128, RTEMS_TASK_STORAGE_ALIGNMENT )
@@ -437,7 +504,7 @@ test-support: |
static void StorageFree( void *ptr )
{
T_eq_ptr( ptr, task_storage );
- ++RtemsTaskReqConstructErrors_Instance.storage_free_calls;
+ ++RtemsTaskReqConstruct_Instance.storage_free_calls;
}
static rtems_status_code Create( void *arg, uint32_t *id )
@@ -460,8 +527,8 @@ test-support: |
(void) executing;
(void) created;
- ++RtemsTaskReqConstructErrors_Instance.create_extension_calls;
- return RtemsTaskReqConstructErrors_Instance.create_extension_status;
+ ++RtemsTaskReqConstruct_Instance.create_extension_calls;
+ return RtemsTaskReqConstruct_Instance.create_extension_status;
}
static bool SecondThreadCreate( rtems_tcb *executing, rtems_tcb *created )
@@ -469,20 +536,81 @@ test-support: |
(void) executing;
(void) created;
- ++RtemsTaskReqConstructErrors_Instance.create_extension_calls;
+ ++RtemsTaskReqConstruct_Instance.create_extension_calls;
return true;
}
static void ThreadDelete( rtems_tcb *executing, rtems_tcb *deleted )
{
+ Context *ctx;
+
(void) executing;
- (void) deleted;
- ++RtemsTaskReqConstructErrors_Instance.delete_extension_calls;
+ ctx = &RtemsTaskReqConstruct_Instance;
+
+ if ( deleted->Object.id == ctx->zombie_id ) {
+ ++ctx->delete_zombie_extension_calls;
+ } else {
+ ++ctx->delete_extension_calls;
+ }
+ }
+
+ #if defined(RTEMS_SMP)
+ static void PreemptionIntervention( void *arg )
+ {
+ Context *ctx;
+ unsigned int ticket;
+
+ ctx = arg;
+ T_false( ctx->zombie_ready );
+ ctx->zombie_ready = true;
+
+ do {
+ ticket = _Atomic_Load_uint(
+ &_Thread_Zombies.Lock.Lock.Ticket_lock.now_serving,
+ ATOMIC_ORDER_RELAXED
+ );
+ } while ( ( ticket - ctx->thread_zombie_ticket ) < 2 );
+
+ T_busy( 100 );
+ }
+
+ static void ThreadTerminate( rtems_tcb *executing )
+ {
+ Context *ctx;
+
+ ctx = &RtemsTaskReqConstruct_Instance;
+
+ if (
+ ctx->scheduler_b_id != INVALID_ID &&
+ ctx->zombie_id == executing->Object.id
+ ) {
+ /*
+ * We use the ticket lock of the thread zombie registry to delay the thread
+ * dispatch and provoke an executing thread in
+ * _Thread_Kill_zombies(). The first acquire is done in the
+ * rtems_task_exit() below. The second acquire is done in
+ * _Thread_Kill_zombies().
+ */
+ ctx->thread_zombie_ticket = _Atomic_Fetch_add_uint(
+ &_Thread_Zombies.Lock.Lock.Ticket_lock.now_serving,
+ 0,
+ ATOMIC_ORDER_RELAXED
+ );
+ SetPreemptionIntervention(
+ _Per_CPU_Get_snapshot(),
+ PreemptionIntervention,
+ ctx
+ );
+ }
}
+ #endif
static const rtems_extensions_table extensions[] = {
{
+ #if defined(RTEMS_SMP)
+ .thread_terminate = ThreadTerminate,
+ #endif
.thread_create = ThreadCreate,
.thread_delete = ThreadDelete
}, {
@@ -490,7 +618,35 @@ test-support: |
.thread_delete = ThreadDelete
}
};
-test-target: testsuites/validation/tc-task-construct-errors.c
+
+ static void ZombieTask( rtems_task_argument arg )
+ {
+ (void) arg;
+ rtems_task_exit();
+ }
+
+ static void PrepareZombie( Context *ctx )
+ {
+ bool create_extension_status;
+
+ create_extension_status = ctx->create_extension_status;
+ ctx->create_extension_status = true;
+ ctx->zombie_id = CreateTask( "ZOMB", PRIO_HIGH );
+ ctx->create_extension_status = create_extension_status;
+ #if defined(RTEMS_SMP)
+ if ( ctx->scheduler_b_id != INVALID_ID ) {
+ ctx->zombie_ready = false;
+ SetScheduler( ctx->zombie_id, ctx->scheduler_b_id, PRIO_NORMAL );
+ }
+ #endif
+ StartTask( ctx->zombie_id, ZombieTask, ctx );
+ #if defined(RTEMS_SMP)
+ while ( !ctx->zombie_ready ) {
+ /* Wait */
+ }
+ #endif
+ }
+test-target: testsuites/validation/tc-task-construct.c
test-teardown:
brief: null
code: |
@@ -501,6 +657,8 @@ test-teardown:
sc = rtems_extension_delete( ctx->extension_ids[ 1 ] );
T_rsc_success( sc );
+
+ RestoreRunnerPriority();
description: null
text: ${.:text-template}
transition-map:
@@ -513,7 +671,7 @@ transition-map:
then: InvAddr
- if:
pre-conditions:
- Name: Inv
+ Name: Invalid
then: InvName
- if:
pre-conditions:
@@ -521,12 +679,12 @@ transition-map:
then: InvAddr
- if:
pre-conditions:
- Prio: Zero
- SysTsk: 'No'
+ Priority: Zero
+ SystemTask: 'No'
then: InvPrio
- if:
pre-conditions:
- Prio: Inv
+ Priority: Invalid
then: InvPrio
- if:
pre-conditions:
@@ -534,15 +692,15 @@ transition-map:
then: TooMany
- if:
pre-conditions:
- TLS: Small
+ TLS: TooSmall
then: InvSize
- if:
pre-conditions:
- Stack: Small
+ Stack: TooSmall
then: InvSize
- if:
pre-conditions:
- Ext: Err
+ Create: Error
then: Unsat
- else: Ok
Name:
@@ -551,13 +709,13 @@ transition-map:
Status: Ok
then: Valid
- else: Invalid
- IdVar:
+ IdObj:
- if:
post-conditions:
Status: Ok
then: Set
- else: Nop
- CreateExt:
+ CreateNew:
- if:
post-conditions:
Status: Ok
@@ -566,14 +724,33 @@ transition-map:
post-conditions:
Status: Unsat
then: UpToFailing
- - else: 'No'
- DelExt:
+ - else: Nop
+ DeleteNew:
- if:
post-conditions:
Status: Unsat
+ then: All
+ - else: Nop
+ KillZombies:
+ - if:
+ - pre-conditions:
+ Config: Valid
+ Name: Valid
+ Id: Valid
+ SystemTask: 'Yes'
+ Priority:
+ - Valid
+ - Zero
+ - pre-conditions:
+ Config: Valid
+ Name: Valid
+ Id: Valid
+ SystemTask: 'No'
+ Priority:
+ - Valid
then: 'Yes'
- else: 'No'
- StoFree:
+ StorageFree:
- if:
post-conditions:
Status: Unsat
@@ -581,11 +758,11 @@ transition-map:
- else: 'No'
pre-conditions:
Config: all
- Ext: all
+ Create: all
Id: all
Name: all
- SysTsk: all
- Prio: all
+ SystemTask: all
+ Priority: all
Stack: all
TLS: all
Free: all