diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2021-05-19 08:17:04 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2021-05-21 08:42:49 +0200 |
commit | 096c68f868d5b0fc2cc5186f7fd1f2eb9e24e62d (patch) | |
tree | 7ee0022ea08d705f715cad760aeb75d1daa316b0 | |
parent | spec: Fix format (diff) | |
download | rtems-central-096c68f868d5b0fc2cc5186f7fd1f2eb9e24e62d.tar.bz2 |
spec: Specify rtems_task_exit()
-rw-r--r-- | spec/rtems/task/req/exit.yml | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/spec/rtems/task/req/exit.yml b/spec/rtems/task/req/exit.yml new file mode 100644 index 00000000..46bf4d32 --- /dev/null +++ b/spec/rtems/task/req/exit.yml @@ -0,0 +1,536 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) +enabled-by: true +functional-type: action +links: +- role: interface-function + uid: ../if/exit +post-conditions: +- name: FatalError + states: + - name: 'Yes' + test-code: | + T_eq_u32( ctx->fatal_extension_calls, 1 ); + text: | + The fatal error with a fatal source of + ${/score/interr/if/internal-error-core:/name} and a fatal code of + ${/score/interr/if/bad-thread-dispatch-disable-level:/name} shall occur + by the ${../if/exit:/name} call. + - name: Nop + test-code: | + T_eq_u32( ctx->fatal_extension_calls, 0 ); + text: | + No fatal error shall occur by the ${../if/exit:/name} call. + test-epilogue: null + test-prologue: null +- name: DeleteExtensions + states: + - name: Nop + test-code: | + T_eq_u32( ctx->delete_extension_calls, 0 ); + text: | + The thread delete user extensions shall not be invoked by the + ${../if/exit:/name} call. + test-epilogue: null + test-prologue: null +- name: RestartExtensions + states: + - name: Nop + test-code: | + T_eq_u32( ctx->restart_extension_calls, 0 ); + text: | + The thread restart user extensions shall not be invoked by the + ${../if/exit:/name} call. + test-epilogue: null + test-prologue: null +- name: TerminateExtensions + states: + - name: 'Yes' + test-code: | + if ( ctx->protected ) { + T_eq_u32( ctx->terminate_extension_calls, 2 ); + } else { + T_eq_u32( ctx->terminate_extension_calls, 1 ); + } + text: | + The thread terminate user extensions shall be invoked by the + ${../if/exit:/name} call. + - name: Nop + test-code: | + T_eq_u32( ctx->terminate_extension_calls, 0 ); + text: | + The thread terminate user extensions shall not be invoked by the + ${../if/exit:/name} call. + test-epilogue: null + test-prologue: null +- name: Block + states: + - name: 'Yes' + test-code: | + event = T_scheduler_next_any( &ctx->scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_BLOCK ); + T_eq_u32( event->thread->Object.id, ctx->worker_id ); + + if ( ctx->terminating ) { + event = T_scheduler_next_any( &ctx->scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_UNBLOCK ); + T_eq_u32( event->thread->Object.id, ctx->deleter_id ); + + event = T_scheduler_next_any( &ctx->scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_BLOCK ); + T_eq_u32( event->thread->Object.id, ctx->deleter_id ); + } + + event = T_scheduler_next_any( &ctx->scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_NOP ); + text: | + The calling task shall be blocked exactly once by the ${../if/exit:/name} + call. + - name: Nop + test-code: | + T_eq_sz( ctx->scheduler_log.header.recorded, 0 ); + text: | + No task shall be blocked by the ${../if/exit:/name} call. + test-epilogue: null + test-prologue: | + const T_scheduler_event *event; + size_t index; + + index = 0; +- name: ID + states: + - name: Valid + test-code: | + T_rsc_success( sc ); + text: | + The object identifier of the calling task shall be valid. + - name: Invalid + test-code: | + T_rsc( sc, RTEMS_INVALID_ID ); + text: | + The object identifier of the calling task shall be invalid. + test-epilogue: null + test-prologue: | + rtems_status_code sc; + rtems_id id; + + sc = rtems_task_get_scheduler( ctx->worker_id, &id ); +- name: Delete + states: + - name: NextAllocate + test-code: | + T_eq_u32( ctx->delete_extension_calls, 1 ); + text: | + The calling task shall be deleted by the next directive which allocates a + task. + - name: Nop + test-code: | + T_eq_u32( ctx->delete_extension_calls, 0 ); + text: | + The calling task shall not be deleted by the next directive which + allocates a task. + test-epilogue: | + DeleteTask( id ); + test-prologue: | + rtems_id id; + + id = CreateTask( "TEMP", PRIO_LOW ); +pre-conditions: +- name: Restarting + states: + - name: 'Yes' + test-code: | + ctx->restarting = true; + text: | + While the calling task is restarting. + - name: 'No' + test-code: | + ctx->restarting = false; + text: | + While the calling task is not restarting. + test-epilogue: null + test-prologue: null +- name: Terminating + states: + - name: 'Yes' + test-code: | + ctx->terminating = true; + text: | + While the calling task is terminating. + - name: 'No' + test-code: | + ctx->terminating = false; + text: | + While the calling task is not terminating. + test-epilogue: null + test-prologue: null +- name: Protected + states: + - name: 'Yes' + test-code: | + ctx->protected = true; + text: | + While the thread life of the calling task is protected. + - name: 'No' + test-code: | + ctx->protected = false; + text: | + While the thread life of the calling task is not protected. + test-epilogue: null + test-prologue: null +- name: ThreadDispatch + states: + - name: Enabled + test-code: | + ctx->dispatch_disabled = false; + text: | + While thread dispatching is enabled for the calling task. + - name: Disabled + test-code: | + ctx->dispatch_disabled = true; + text: | + While thread dispatching is disabled for the calling task. + test-epilogue: null + test-prologue: null +rationale: null +references: [] +requirement-type: functional +skip-reasons: {} +test-action: | + rtems_status_code sc; + + ctx->delete_worker_expected = false; + ctx->worker_id = CreateTask( "WORK", PRIO_NORMAL ); + ctx->delete_worker_expected = true; + + StartTask( ctx->worker_id, Worker, ctx ); + + /* Let the worker catch signals and set the thread life protection state */ + Yield(); + + sc = rtems_signal_send( ctx->worker_id, RTEMS_SIGNAL_0 ); + T_rsc_success( sc ); + + if ( ctx->restarting ) { + sc = rtems_task_restart( ctx->worker_id, (rtems_task_argument) ctx ); + T_rsc_success( sc ); + } + + if ( ctx->terminating ) { + sc = rtems_task_restart( ctx->deleter_id, (rtems_task_argument) ctx ); + T_rsc_success( sc ); + } else { + Yield(); + } + + if ( !ctx->dispatch_disabled ) { + T_scheduler_log *log; + + log = T_scheduler_record( NULL ); + T_eq_ptr( &log->header, &ctx->scheduler_log.header ); + } +test-brief: null +test-cleanup: | + if ( ctx->dispatch_disabled ) { + DeleteTask( ctx->worker_id ); + } +test-context: +- brief: | + This member provides the scheduler operation records. + description: null + member: | + T_scheduler_log_4 scheduler_log +- brief: | + This member contains the identifier of the runner task. + description: null + member: | + rtems_id runner_id +- brief: | + This member contains the identifier of the worker task. + description: null + member: | + rtems_id worker_id +- brief: | + This member contains the identifier of the deleter task. + description: null + member: | + rtems_id deleter_id +- brief: | + This member contains the identifier of the test user extensions. + description: null + member: | + rtems_id extension_id +- brief: | + This member contains the count of fatal extension calls. + description: null + member: | + uint32_t fatal_extension_calls +- brief: | + This member contains the count of thread delete extension calls. + description: null + member: | + uint32_t delete_extension_calls +- brief: | + This member contains the count of thread restart extension calls. + description: null + member: | + uint32_t restart_extension_calls +- brief: | + This member contains the count of thread terminate extension calls. + description: null + member: | + uint32_t terminate_extension_calls +- brief: | + If this member is true, then the thread life of the worker is protected + before the ${../if/exit:/name} call. + description: null + member: | + bool protected +- brief: | + If this member is true, then the worker locked the allocator. + description: null + member: | + bool allocator_locked +- brief: | + If this member is true, then the worker is restarting before the + ${../if/exit:/name} call. + description: null + member: | + bool restarting +- brief: | + If this member is true, then the worker is terminating before the + ${../if/exit:/name} call. + description: null + member: | + bool terminating +- brief: | + If this member is true, then thread dispatching is disabled by the worker + task before the ${../if/exit:/name} call. + description: null + member: | + bool dispatch_disabled +- brief: | + If this member is true, then it is expected to delete the worker. + description: null + member: | + bool delete_worker_expected +test-context-support: null +test-description: null +test-header: null +test-includes: +- rtems.h +- rtems/test-scheduler.h +- rtems/score/apimutex.h +- rtems/score/threaddispatch.h +test-local-includes: +- tx-support.h +test-prepare: null +test-setup: + brief: null + code: | + rtems_status_code sc; + + ctx->runner_id = rtems_task_self(); + + sc = rtems_extension_create( + rtems_build_name( 'T', 'E', 'S', 'T' ), + &extensions, + &ctx->extension_id + ); + T_rsc_success( sc ); + + SetFatalExtension( Fatal ); + SetSelfPriority( PRIO_NORMAL ); + + ctx->deleter_id = CreateTask( "DELE", PRIO_HIGH ); + StartTask( ctx->deleter_id, Deleter, NULL ); + description: null +test-stop: null +test-support: | + typedef RtemsTaskReqExit_Context Context; + + static void Signal( rtems_signal_set signals ) + { + Context *ctx; + T_scheduler_log *log; + Thread_Life_state life_state; + + (void) signals; + ctx = T_fixture_context(); + + if ( ctx->dispatch_disabled ) { + _Thread_Dispatch_disable(); + } + + /* Check that the thread life state was prepared correctly */ + life_state = GetExecuting()->Life.state; + T_eq( ctx->protected, ( life_state & THREAD_LIFE_PROTECTED ) != 0 ); + T_eq( ctx->restarting, ( life_state & THREAD_LIFE_RESTARTING ) != 0 ); + T_eq( ctx->terminating, ( life_state & THREAD_LIFE_TERMINATING ) != 0 ); + + log = T_scheduler_record_4( &ctx->scheduler_log ); + T_null( log ); + + ctx->delete_extension_calls = 0; + ctx->fatal_extension_calls = 0; + ctx->restart_extension_calls = 0; + ctx->terminate_extension_calls = 0; + + rtems_task_exit(); + } + + static void Deleter( rtems_task_argument arg ) + { + Context *ctx; + + ctx = (Context *) arg; + + if ( ctx != NULL ) { + DeleteTask( ctx->worker_id ); + } + + SuspendSelf(); + } + + static void Worker( rtems_task_argument arg ) + { + Context *ctx; + rtems_status_code sc; + + ctx = (Context *) arg; + + sc = rtems_signal_catch( Signal, RTEMS_NO_ASR ); + T_rsc_success( sc ); + + if ( ctx->protected ) { + _RTEMS_Lock_allocator(); + ctx->allocator_locked = true; + } + + Yield(); + } + + static void UnlockAllocator( Context *ctx ) + { + if ( ctx->allocator_locked ) { + ctx->allocator_locked = false; + _RTEMS_Unlock_allocator(); + } + } + + static void Fatal( + rtems_fatal_source source, + bool always_set_to_false, + rtems_fatal_code code + ) + { + Context *ctx; + T_scheduler_log *log; + Per_CPU_Control *cpu_self; + + ctx = T_fixture_context(); + ++ctx->fatal_extension_calls; + + T_eq_int( source, INTERNAL_ERROR_CORE ); + T_false( always_set_to_false ); + T_eq_ulong( code, INTERNAL_ERROR_BAD_THREAD_DISPATCH_DISABLE_LEVEL ); + T_assert_eq_int( ctx->fatal_extension_calls, 1 ); + + log = T_scheduler_record( NULL ); + T_eq_ptr( &log->header, &ctx->scheduler_log.header ); + + UnlockAllocator( ctx ); + SuspendSelf(); + + cpu_self = _Per_CPU_Get(); + _Thread_Dispatch_unnest( cpu_self ); + _Thread_Dispatch_direct_no_return( cpu_self ); + } + + static void ThreadDelete( rtems_tcb *executing, rtems_tcb *deleted ) + { + Context *ctx; + + ctx = T_fixture_context(); + ++ctx->delete_extension_calls; + + T_eq_u32( executing->Object.id, ctx->runner_id ); + + if ( ctx->delete_worker_expected ) { + T_eq_u32( deleted->Object.id, ctx->worker_id ); + } + } + + static void ThreadRestart( rtems_tcb *executing, rtems_tcb *restarted ) + { + Context *ctx; + + ctx = T_fixture_context(); + ++ctx->restart_extension_calls; + } + + static void ThreadTerminate( rtems_tcb *executing ) + { + Context *ctx; + + ctx = T_fixture_context(); + ++ctx->terminate_extension_calls; + + T_eq_u32( executing->Object.id, ctx->worker_id ); + + UnlockAllocator( ctx ); + } + + static const rtems_extensions_table extensions = { + .thread_delete = ThreadDelete, + .thread_restart = ThreadRestart, + .thread_terminate = ThreadTerminate + }; +test-target: testsuites/validation/tc-task-exit.c +test-teardown: + brief: null + code: | + rtems_status_code sc; + + sc = rtems_extension_delete( ctx->extension_id ); + T_rsc_success( sc ); + + SetFatalExtension( NULL ); + DeleteTask( ctx->deleter_id ); + RestoreRunnerASR(); + RestoreRunnerPriority(); + description: null +text: ${.:text-template} +transition-map: +- enabled-by: true + post-conditions: + FatalError: Nop + DeleteExtensions: Nop + RestartExtensions: Nop + TerminateExtensions: 'Yes' + Block: 'Yes' + ID: Invalid + Delete: NextAllocate + pre-conditions: + Restarting: all + Terminating: all + Protected: all + ThreadDispatch: + - Enabled +- enabled-by: true + post-conditions: + FatalError: 'Yes' + DeleteExtensions: Nop + RestartExtensions: Nop + TerminateExtensions: Nop + Block: Nop + ID: Valid + Delete: Nop + pre-conditions: + Restarting: all + Terminating: all + Protected: all + ThreadDispatch: + - Disabled +type: requirement |