diff --git a/include/portability/cmsis_os.h b/include/portability/cmsis_os.h index 1af863e6678..c6ef5ccff0c 100644 --- a/include/portability/cmsis_os.h +++ b/include/portability/cmsis_os.h @@ -67,6 +67,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" @@ -172,6 +173,8 @@ typedef struct os_thread_def { struct k_poll_signal *poll_signal; struct k_poll_event *poll_event; int32_t signal_results; + ///< a bitarray used to indicate whether the thread is used or not, 0: unused, 1: used + void *status_mask; } osThreadDef_t; /// Timer Definition structure contains timer parameters. @@ -288,8 +291,11 @@ static K_THREAD_STACK_ARRAY_DEFINE(stacks_##name, instances, CONFIG_CMSIS_THREAD static struct k_thread cm_thread_##name[instances]; \ static struct k_poll_signal wait_signal_##name; \ static struct k_poll_event wait_events_##name; \ +static SYS_BITARRAY_DEFINE(bitarray_##name, instances); \ static osThreadDef_t os_thread_def_##name = \ -{ (name), (priority), (instances), (stacksz), (void *)(stacks_##name), (cm_thread_##name), (&wait_signal_##name), (&wait_events_##name), 0 } +{ (name), (priority), (instances), (stacksz), (void *)(stacks_##name), \ + (cm_thread_##name), (&wait_signal_##name), \ + (&wait_events_##name), 0, (void *)(&bitarray_##name)} #endif /// Access a Thread definition. diff --git a/kernel/sched.c b/kernel/sched.c index e7393278529..a9499af9b36 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1460,6 +1460,10 @@ static inline void unpend_all(_wait_q_t *wait_q) } } +#ifdef CONFIG_CMSIS_RTOS_V1 +extern void z_thread_cmsis_status_mask_clear(struct k_thread *thread); +#endif + static void end_thread(struct k_thread *thread) { /* We hold the lock, and the thread is known not to be running @@ -1482,6 +1486,10 @@ static void end_thread(struct k_thread *thread) z_thread_monitor_exit(thread); +#ifdef CONFIG_CMSIS_RTOS_V1 + z_thread_cmsis_status_mask_clear(thread); +#endif + #ifdef CONFIG_USERSPACE z_mem_domain_exit_thread(thread); z_thread_perms_all_clear(thread); diff --git a/subsys/portability/cmsis_rtos_v1/Kconfig b/subsys/portability/cmsis_rtos_v1/Kconfig index fdfbc199823..f49c19d8d8a 100644 --- a/subsys/portability/cmsis_rtos_v1/Kconfig +++ b/subsys/portability/cmsis_rtos_v1/Kconfig @@ -5,6 +5,7 @@ config CMSIS_RTOS_V1 bool "CMSIS RTOS v1 API" depends on THREAD_CUSTOM_DATA depends on POLL + depends on THREAD_STACK_INFO help This enables CMSIS RTOS v1 API support. This is an OS-integration layer which allows applications using CMSIS RTOS APIs to build on diff --git a/subsys/portability/cmsis_rtos_v1/cmsis_thread.c b/subsys/portability/cmsis_rtos_v1/cmsis_thread.c index b6320896023..cea50046269 100644 --- a/subsys/portability/cmsis_rtos_v1/cmsis_thread.c +++ b/subsys/portability/cmsis_rtos_v1/cmsis_thread.c @@ -34,6 +34,23 @@ static void zephyr_thread_wrapper(void *arg1, void *arg2, void *arg3) fun_ptr(arg1); } +/* clear related bit in cmsis thread status bitarray + * when terminating a thread + */ +void z_thread_cmsis_status_mask_clear(struct k_thread *thread) +{ + uint32_t offset, instance; + + osThreadDef_t *thread_def = (osThreadDef_t *)(thread->custom_data); + + if (thread_def != NULL) { + /* get thread instance index according to stack address */ + offset = thread->stack_info.start - (uintptr_t)thread_def->stack_mem; + instance = offset / K_THREAD_STACK_LEN(CONFIG_CMSIS_THREAD_MAX_STACK_SIZE); + sys_bitarray_clear_bit((sys_bitarray_t *)(thread_def->status_mask), instance); + } +} + /** * @brief Create a new thread. */ @@ -43,6 +60,8 @@ osThreadId osThreadCreate(const osThreadDef_t *thread_def, void *arg) uint32_t prio; k_tid_t tid; uint32_t stacksz; + int ret; + size_t instance; k_thread_stack_t (*stk_ptr)[K_THREAD_STACK_LEN(CONFIG_CMSIS_THREAD_MAX_STACK_SIZE)]; @@ -67,6 +86,13 @@ osThreadId osThreadCreate(const osThreadDef_t *thread_def, void *arg) (thread_def->tpriority <= osPriorityRealtime), "invalid priority\n"); + /* get an available thread instance */ + ret = sys_bitarray_alloc((sys_bitarray_t *)(thread_def->status_mask), + 1, &instance); + if (ret != 0) { + return NULL; + } + stacksz = thread_def->stacksize; if (stacksz == 0U) { stacksz = CONFIG_CMSIS_THREAD_MAX_STACK_SIZE; @@ -77,17 +103,22 @@ osThreadId osThreadCreate(const osThreadDef_t *thread_def, void *arg) K_POLL_MODE_NOTIFY_ONLY, thread_def->poll_signal); cm_thread = thread_def->cm_thread; - atomic_dec((atomic_t *)&thread_def->instances); stk_ptr = thread_def->stack_mem; prio = cmsis_to_zephyr_priority(thread_def->tpriority); k_thread_custom_data_set((void *)thread_def); - tid = k_thread_create(&cm_thread[thread_def->instances], - stk_ptr[thread_def->instances], stacksz, + tid = k_thread_create(&cm_thread[instance], + stk_ptr[instance], stacksz, (k_thread_entry_t)zephyr_thread_wrapper, (void *)arg, NULL, thread_def->pthread, prio, 0, K_NO_WAIT); + /* make custom_data pointer of thread point to its source thread_def, + * then we can use it to release thread instances + * when terminating threads + */ + tid->custom_data = (void *)thread_def; + return ((osThreadId)tid); } diff --git a/tests/subsys/portability/cmsis_rtos_v1/prj.conf b/tests/subsys/portability/cmsis_rtos_v1/prj.conf index 49d0bda8da7..f08e6d07ca4 100644 --- a/tests/subsys/portability/cmsis_rtos_v1/prj.conf +++ b/tests/subsys/portability/cmsis_rtos_v1/prj.conf @@ -6,3 +6,4 @@ CONFIG_MAX_THREAD_BYTES=4 CONFIG_IRQ_OFFLOAD=y CONFIG_CMSIS_THREAD_MAX_STACK_SIZE=2048 CONFIG_SMP=n +CONFIG_THREAD_STACK_INFO=y diff --git a/tests/subsys/portability/cmsis_rtos_v1/src/thread_instance.c b/tests/subsys/portability/cmsis_rtos_v1/src/thread_instance.c index 262bea23920..6090d2f24a3 100644 --- a/tests/subsys/portability/cmsis_rtos_v1/src/thread_instance.c +++ b/tests/subsys/portability/cmsis_rtos_v1/src/thread_instance.c @@ -26,6 +26,7 @@ osThreadDef(thread_inst_check, osPriorityNormal, 3, STACKSZ); void test_thread_instances(void) { osThreadId id1, id2, id3, id4; + osStatus status; id1 = osThreadCreate(osThread(thread_inst_check), NULL); zassert_true(id1 != NULL, "Failed creating thread_inst_check"); @@ -38,4 +39,14 @@ void test_thread_instances(void) id4 = osThreadCreate(osThread(thread_inst_check), NULL); zassert_true(id4 == NULL, "Something wrong with thread instances"); + + status = osThreadTerminate(id2); + zassert_true(status == osOK, "Error terminating thread_inst_check"); + + /* after terminating thread id2, when creating a new thread, + * it should re-use the available thread instance of id2. + */ + id4 = osThreadCreate(osThread(thread_inst_check), NULL); + zassert_true(id4 != NULL, "Failed creating thread_inst_check"); + zassert_true(id2 == id4, "Error creating thread_inst_check"); }