SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause copyrights: - Copyright (C) 2020, 2021 embedded brains GmbH (http://www.embedded-brains.de) enabled-by: true functional-type: action links: - role: interface-function uid: ../if/construct post-conditions: - name: Status states: - name: Ok test-code: | T_rsc_success( ctx->status ); text: | The return status of ${../if/construct:/name} shall be ${../../status/if/successful:/name}. - name: InvAddr test-code: | T_rsc( ctx->status, RTEMS_INVALID_ADDRESS ); text: | The return status of ${../if/construct:/name} shall be ${../../status/if/invalid-address:/name}. - name: InvName test-code: | T_rsc( ctx->status, RTEMS_INVALID_NAME ); text: | The return status of ${../if/construct:/name} shall be ${../../status/if/invalid-name:/name}. - name: InvPrio test-code: | T_rsc( ctx->status, RTEMS_INVALID_PRIORITY ); text: | The return status of ${../if/construct:/name} shall be ${../../status/if/invalid-priority:/name}. - name: InvSize test-code: | T_rsc( ctx->status, RTEMS_INVALID_SIZE ); text: | The return status of ${../if/construct:/name} shall be ${../../status/if/invalid-size:/name}. - name: TooMany test-code: | T_rsc( ctx->status, RTEMS_TOO_MANY ); text: | The return status of ${../if/construct:/name} shall be ${../../status/if/too-many:/name}. - name: Unsat test-code: | T_rsc( ctx->status, RTEMS_UNSATISFIED ); text: | The return status of ${../if/construct:/name} shall be ${../../status/if/unsatisfied:/name}. test-epilogue: null test-prologue: null - name: Name states: - name: Valid test-code: | id = 0; sc = rtems_task_ident( NAME, RTEMS_SEARCH_LOCAL_NODE, &id ); T_rsc_success( sc ); T_eq_u32( id, ctx->id_obj ); text: | The unique object name shall identify the task constructed by the ${../if/construct:/name} call. - name: Invalid test-code: | sc = rtems_task_ident( NAME, RTEMS_SEARCH_LOCAL_NODE, &id ); T_rsc( sc, RTEMS_INVALID_NAME ); text: | The unique object name shall not identify a task. test-epilogue: null test-prologue: | rtems_status_code sc; rtems_id id; - name: IdObj states: - name: Set test-code: | 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 identifier of the constructed task after the return of the ${../if/construct:/name} call. - name: Nop test-code: | 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: CreateNew states: - name: All test-code: | T_eq_u32( ctx->create_extension_calls, 2 ); text: | 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 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 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: DeleteNew states: - name: All test-code: | T_eq_u32( ctx->delete_extension_calls, 2 ); text: | 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 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: StorageFree states: - name: 'Yes' test-code: | T_eq_u32( ctx->storage_free_calls, 1 ); text: | The storage free handler of the task configuration shall be invoked during the ${../if/construct:/name} call. - name: 'No' test-code: | T_eq_u32( ctx->storage_free_calls, 0 ); text: | The storage free handler of the task configuration shall not be invoked during the ${../if/construct:/name} call. test-epilogue: null test-prologue: null - name: FloatingPoint states: - name: 'Yes' test-code: | /* Validated by WorkerTask() */ text: | Where threads have a dedicated floating-point context, the task constructed by the ${../if/construct:/name} call shall be able to use the floating-point unit. - name: 'No' test-code: | /* Validated by WorkerTask() */ text: | Where threads have a dedicated floating-point context, the task constructed by the ${../if/construct:/name} call shall not be able to use the floating-point unit. test-epilogue: null test-prologue: null pre-conditions: - name: Config states: - name: Valid test-code: | ctx->config = &ctx->config_obj; text: | While the ${../if/construct:/params[0]/name} parameter references an object of type ${../if/config:/name}. - name: 'Null' test-code: | ctx->config = NULL; text: | While the ${../if/construct:/params[0]/name} parameter is ${/c/if/null:/name}. test-epilogue: null test-prologue: null - name: Name states: - name: Valid test-code: | ctx->config_obj.name = NAME; text: | While the name of the task configuration is valid. - name: Invalid test-code: | ctx->config_obj.name = 0; text: | While the name of the task configuration is invalid. test-epilogue: null test-prologue: null - name: Id states: - name: Valid test-code: | ctx->id = &ctx->id_obj; text: | While the ${../if/construct:/params[1]/name} parameter references an object of type ${../../type/if/id:/name}. - name: 'Null' test-code: | ctx->id = NULL; text: | While the ${../if/construct:/params[1]/name} parameter is ${/c/if/null:/name}. test-epilogue: null test-prologue: null - name: SystemTask states: - name: 'Yes' test-code: | ctx->config_obj.attributes |= RTEMS_SYSTEM_TASK; text: | While the attributes of the task configuration specifies a system task. - name: 'No' test-code: | ctx->config_obj.attributes |= RTEMS_APPLICATION_TASK; text: | While the attributes of the task configuration specifies an application task. test-epilogue: null test-prologue: null - name: Priority states: - name: Valid test-code: | ctx->config_obj.initial_priority = PRIO_HIGH; text: | While the initial priority of the task configuration is valid and non-zero. - name: Zero test-code: | ctx->config_obj.initial_priority = 0; text: | While the initial priority of the task configuration is zero. - name: Invalid test-code: | ctx->config_obj.initial_priority = 0xffffffff; text: | While the initial priority of the task configuration is invalid. test-epilogue: null test-prologue: null - name: Free states: - name: 'Yes' test-code: | /* Nothing to do */ text: | While the system has at least one inactive task object available. - name: 'No' test-code: | ctx->seized_objects = T_seize_objects( Create, ctx ); text: | While the system has no inactive task object available. test-epilogue: null test-prologue: null - name: TLS states: - name: Enough test-code: | 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: TooSmall test-code: | 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. test-epilogue: null test-prologue: null - name: Stack states: - name: Enough test-code: | ctx->stack_size = RTEMS_MINIMUM_STACK_SIZE; text: | While the task stack size of the task configuration is greater than or equal to the configured minimum size. - name: TooSmall test-code: | ctx->stack_size = 0; text: | While the task stack size of the task configuration is less than the configured minimum size. test-epilogue: null test-prologue: null - name: Create states: - name: Ok test-code: | ctx->create_extension_status = true; text: | 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 thread create user extensions fails. test-epilogue: null test-prologue: null - name: FloatingPoint states: - name: 'Yes' test-code: | ctx->config_obj.attributes |= RTEMS_FLOATING_POINT; text: | While the attributes of the task configuration specifies a floating-point task. - name: 'No' test-code: | ctx->config_obj.attributes |= RTEMS_NO_FLOATING_POINT; text: | While the attributes of the task configuration specifies a non-floating-point task. test-epilogue: null test-prologue: null rationale: null 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_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 ); if ( ctx->status == RTEMS_SUCCESSFUL ) { StartTask( ctx->id_obj, WorkerTask, ctx ); } test-brief: null test-cleanup: | if ( ctx->id_obj != INVALID_ID ) { rtems_status_code sc; sc = rtems_task_delete( ctx->id_obj ); T_rsc_success( sc ); 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: | rtems_status_code status - brief: null description: null member: | const rtems_task_config *config - brief: null description: null member: | rtems_task_config config_obj - brief: null description: null member: | rtems_id zombie_id - brief: null description: null member: | rtems_id *id - brief: null description: null member: | rtems_id id_obj - brief: null description: null member: | bool create_extension_status - brief: null description: null member: | uint32_t create_extension_calls - brief: null description: null member: | uint32_t delete_extension_calls - 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 member: | size_t stack_size - brief: null description: null member: | rtems_id extension_ids[ 2 ] - brief: null description: null member: | void *seized_objects test-context-support: null test-description: null test-header: null test-includes: - rtems.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: | 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_object; RTEMS_OBFUSCATE_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' ), &extensions[ 0 ], &ctx->extension_ids[ 0 ] ); T_rsc_success( sc ); sc = rtems_extension_create( rtems_build_name( 'E', 'X', 'T', '2' ), &extensions[ 1 ], &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 RtemsTaskReqConstruct_Context Context; static volatile _Thread_local int tls_object; #define MAX_TLS_SIZE RTEMS_ALIGN_UP( 128, RTEMS_TASK_STORAGE_ALIGNMENT ) RTEMS_ALIGNED( RTEMS_TASK_STORAGE_ALIGNMENT ) static char task_storage[ RTEMS_TASK_STORAGE_SIZE( MAX_TLS_SIZE + RTEMS_MINIMUM_STACK_SIZE, RTEMS_FLOATING_POINT ) ]; static const rtems_task_config seize_task_config = { .name = rtems_build_name( 'S', 'I', 'Z', 'E' ), .initial_priority = 1, .storage_area = task_storage, .storage_size = sizeof( task_storage ), .maximum_thread_local_storage_size = MAX_TLS_SIZE, .initial_modes = RTEMS_DEFAULT_MODES, .attributes = RTEMS_DEFAULT_MODES }; static void StorageFree( void *ptr ) { T_eq_ptr( ptr, task_storage ); ++RtemsTaskReqConstruct_Instance.storage_free_calls; } static rtems_status_code Create( void *arg, uint32_t *id ) { Context *ctx; bool create_extension_status; rtems_status_code sc; ctx = arg; create_extension_status = ctx->create_extension_status; ctx->create_extension_status = true; sc = rtems_task_construct( &seize_task_config, id ); ctx->create_extension_status = create_extension_status; return sc; } static bool ThreadCreate( rtems_tcb *executing, rtems_tcb *created ) { (void) executing; (void) created; ++RtemsTaskReqConstruct_Instance.create_extension_calls; return RtemsTaskReqConstruct_Instance.create_extension_status; } static bool SecondThreadCreate( rtems_tcb *executing, rtems_tcb *created ) { (void) executing; (void) created; ++RtemsTaskReqConstruct_Instance.create_extension_calls; return true; } static void ThreadDelete( rtems_tcb *executing, rtems_tcb *deleted ) { Context *ctx; (void) executing; 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 }, { .thread_create = SecondThreadCreate, .thread_delete = ThreadDelete } }; 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 } static volatile double double_object; static RTEMS_NO_INLINE void UseFloatingPointUnit( void ) { double_object = ( 123.0 * double_object + double_object / 123.0 ) / 2.0; } static void WorkerTask( rtems_task_argument arg ) { Context *ctx; ctx = (Context *) arg; /* * We don't validate here that we cannot use the floating-point unit if the * RTEMS_FLOATING_POINT attribute is not set. This is done elsewhere. * Using the floating-point unit if the RTEMS_FLOATING_POINT attribute is * not set may result in unrecoverable fatal errors. */ if ( ( ctx->config_obj.attributes & RTEMS_FLOATING_POINT ) != 0 ) { UseFloatingPointUnit(); } SuspendSelf(); } test-target: testsuites/validation/tc-task-construct.c test-teardown: brief: null code: | rtems_status_code sc; sc = rtems_extension_delete( ctx->extension_ids[ 0 ] ); T_rsc_success( sc ); sc = rtems_extension_delete( ctx->extension_ids[ 1 ] ); T_rsc_success( sc ); RestoreRunnerPriority(); description: null text: ${.:text-template} transition-map: - enabled-by: true post-conditions: Status: - if: pre-conditions: Config: 'Null' then: InvAddr - if: pre-conditions: Name: Invalid then: InvName - if: pre-conditions: Id: 'Null' then: InvAddr - if: pre-conditions: Priority: Zero SystemTask: 'No' then: InvPrio - if: pre-conditions: Priority: Invalid then: InvPrio - if: pre-conditions: Free: 'No' then: TooMany - if: pre-conditions: TLS: TooSmall then: InvSize - if: pre-conditions: Stack: TooSmall then: InvSize - if: pre-conditions: Create: Error then: Unsat - else: Ok Name: - if: post-conditions: Status: Ok then: Valid - else: Invalid IdObj: - if: post-conditions: Status: Ok then: Set - else: Nop CreateNew: - if: post-conditions: Status: Ok then: All - if: post-conditions: Status: Unsat then: UpToFailing - 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' FloatingPoint: - if: post-conditions: Status: Ok then-specified-by: FloatingPoint - else: N/A StorageFree: - if: post-conditions: Status: Unsat then: 'Yes' - else: 'No' pre-conditions: Config: all Create: all Id: all Name: all SystemTask: all Priority: all Stack: all TLS: all Free: all FloatingPoint: all type: requirement