From ed7a1bf81d9b3b82bbe9ae9232982aab44e632e5 Mon Sep 17 00:00:00 2001 From: Tomasz Gorochowik Date: Wed, 29 May 2019 13:17:48 +0200 Subject: [PATCH 1/3] lib: posix: implement pthread_attr_setstacksize() From IEEE Std 1003.1-2017: The pthread_attr_getstacksize() and pthread_attr_setstacksize() functions, respectively, shall get and set the thread creation stacksize attribute in the attr object. The stacksize attribute shall define the minimum stack size (in bytes) allocated for the created threads stack. The behavior is undefined if the value specified by the attr argument to pthread_attr_getstacksize() or pthread_attr_setstacksize() does not refer to an initialized thread attributes object. Signed-off-by: Tomasz Gorochowik --- include/posix/pthread.h | 1 + lib/posix/pthread.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/posix/pthread.h b/include/posix/pthread.h index 350c28e641b562..a141fa53235408 100644 --- a/include/posix/pthread.h +++ b/include/posix/pthread.h @@ -475,6 +475,7 @@ static inline int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) } int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy); int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); diff --git a/lib/posix/pthread.c b/lib/posix/pthread.c index a2138bbccd339f..02358cfe4b9e87 100644 --- a/lib/posix/pthread.c +++ b/lib/posix/pthread.c @@ -546,6 +546,22 @@ int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) } +/** + * @brief Set stack size attribute in thread attributes object. + * + * See IEEE 1003.1 + */ +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) +{ + if ((attr == NULL) || (attr->initialized == 0U)) { + return EINVAL; + } + + attr->stacksize = stacksize; + return 0; + +} + /** * @brief Get stack attributes in thread attributes object. * From 4ba6667b62f9bf4dc5f2bed58b455feca2bf2b25 Mon Sep 17 00:00:00 2001 From: Tomasz Gorochowik Date: Fri, 24 May 2019 17:39:42 +0200 Subject: [PATCH 2/3] lib: posix: auto allocate pthread stack if needed POSIX 1003.1 spec allows creating pthreads with attrib as NULL. In that case, the stack needs to be auto-allocated. Signed-off-by: Tomasz Gorochowik --- include/posix/pthread.h | 3 ++ lib/posix/Kconfig | 9 ++++ lib/posix/pthread.c | 91 +++++++++++++++++++++++++++++++++-------- 3 files changed, 85 insertions(+), 18 deletions(-) diff --git a/include/posix/pthread.h b/include/posix/pthread.h index a141fa53235408..1b569239fda32e 100644 --- a/include/posix/pthread.h +++ b/include/posix/pthread.h @@ -46,6 +46,9 @@ struct posix_thread { enum pthread_state state; pthread_mutex_t state_lock; pthread_cond_t state_cond; + + /* Auto-allocated stack */ + void *auto_allocated_stack; }; /* Pthread detach/joinable */ diff --git a/lib/posix/Kconfig b/lib/posix/Kconfig index ef8e60638764d4..bfac1e82358a14 100644 --- a/lib/posix/Kconfig +++ b/lib/posix/Kconfig @@ -36,6 +36,15 @@ config MAX_PTHREAD_COUNT help Maximum number of simultaneously active threads in a POSIX application. +config DEFAULT_PTHREAD_STACK_SIZE + int "Default stack size used for pthreads" + default 2048 if COVERAGE_GCOV + default 1024 + help + If pthread_create is called without attributes specifying that an externally + defined stack is to be used, the stack will be allocated automatically with + the size specified in this symbol. + config SEM_VALUE_MAX int "Maximum semaphore limit" default 32767 diff --git a/lib/posix/pthread.c b/lib/posix/pthread.c index 02358cfe4b9e87..82d55353bb3f38 100644 --- a/lib/posix/pthread.c +++ b/lib/posix/pthread.c @@ -120,6 +120,17 @@ static void zephyr_thread_wrapper(void *arg1, void *arg2, void *arg3) pthread_exit(NULL); } +/* Helper function for stack allocation. */ +static void pthread_setup_dynamic_stack(void **newstack, size_t *newsize) +{ + /* If the requested size is 0, use the default stack size */ + if (*newsize == 0) { + *newsize = CONFIG_DEFAULT_PTHREAD_STACK_SIZE; + } + + *newstack = calloc(1, *newsize); +} + /** * @brief Create a new thread. * @@ -135,14 +146,12 @@ int pthread_create(pthread_t *newthread, const pthread_attr_t *attr, u32_t pthread_num; pthread_condattr_t cond_attr; struct posix_thread *thread; + void *newstack; + size_t newstacksize; + const struct pthread_attr_t *newattr; - /* - * FIXME: Pthread attribute must be non-null and it provides stack - * pointer and stack size. So even though POSIX 1003.1 spec accepts - * attrib as NULL but zephyr needs it initialized with valid stack. - */ - if ((attr == NULL) || (attr->initialized == 0U) - || (attr->stack == NULL) || (attr->stacksize == 0)) { + if (attr != NULL && attr->initialized == 0U) { + /* Undefined behavior - don't try to do anything */ return EINVAL; } @@ -161,32 +170,69 @@ int pthread_create(pthread_t *newthread, const pthread_attr_t *attr, return EAGAIN; } - prio = posix_to_zephyr_priority(attr->priority, attr->schedpolicy); + if (attr == NULL) { + /* Use the default parameters */ + newattr = &init_pthread_attrs; + + /* Allocate default stack */ + newstacksize = CONFIG_DEFAULT_PTHREAD_STACK_SIZE; + pthread_setup_dynamic_stack(&newstack, &newstacksize); + + if (newstack == NULL) { + return EAGAIN; + } + + thread->auto_allocated_stack = newstack; + } else { + /* Use the parameters from user */ + newattr = attr; + + if (attr->stack == NULL) { + newstacksize = attr->stacksize; + pthread_setup_dynamic_stack(&newstack, &newstacksize); + + if (newstack == NULL) { + return EAGAIN; + } + + thread->auto_allocated_stack = newstack; + } else { + newstack = attr->stack; + newstacksize = attr->stacksize; + + thread->auto_allocated_stack = NULL; + } + } + + prio = posix_to_zephyr_priority(newattr->priority, + newattr->schedpolicy); thread = &posix_thread_pool[pthread_num]; pthread_mutex_init(&thread->state_lock, NULL); pthread_mutex_init(&thread->cancel_lock, NULL); pthread_mutex_lock(&thread->cancel_lock); - thread->cancel_state = (1 << _PTHREAD_CANCEL_POS) & attr->flags; + thread->cancel_state = (1 << _PTHREAD_CANCEL_POS) & newattr->flags; thread->cancel_pending = 0; pthread_mutex_unlock(&thread->cancel_lock); pthread_mutex_lock(&thread->state_lock); - thread->state = attr->detachstate; + thread->state = newattr->detachstate; pthread_mutex_unlock(&thread->state_lock); pthread_cond_init(&thread->state_cond, &cond_attr); sys_slist_init(&thread->key_list); - *newthread = (pthread_t) k_thread_create(&thread->thread, attr->stack, - attr->stacksize, - (k_thread_entry_t) - zephyr_thread_wrapper, - (void *)arg, NULL, - threadroutine, prio, - (~K_ESSENTIAL & attr->flags), - attr->delayedstart); + *newthread = + (pthread_t) k_thread_create(&thread->thread, + newstack, + newstacksize, + (k_thread_entry_t) + zephyr_thread_wrapper, + (void *)arg, NULL, + threadroutine, prio, + (~K_ESSENTIAL & newattr->flags), + newattr->delayedstart); return 0; } @@ -248,6 +294,10 @@ int pthread_cancel(pthread_t pthread) } pthread_mutex_unlock(&thread->state_lock); + if (thread->auto_allocated_stack != NULL) { + free(thread->auto_allocated_stack); + } + k_thread_abort((k_tid_t) thread); } @@ -382,6 +432,11 @@ void pthread_exit(void *retval) } pthread_mutex_unlock(&self->state_lock); + + if (self->auto_allocated_stack != NULL) { + free(self->auto_allocated_stack); + } + k_thread_abort((k_tid_t)self); } From 0add6ff45d7c064b27f47dc6064e06058ec2202d Mon Sep 17 00:00:00 2001 From: Tomasz Gorochowik Date: Wed, 29 May 2019 13:45:20 +0200 Subject: [PATCH 3/3] tests: posix: pthread: test auto stack allocation For now use automatic stack allocation for just a single thread to minimize the memory required for malloc arena size. Signed-off-by: Tomasz Gorochowik --- tests/posix/common/prj.conf | 1 + tests/posix/common/src/pthread.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/posix/common/prj.conf b/tests/posix/common/prj.conf index 4f6a845b21c841..4f7e9f93fe0b21 100644 --- a/tests/posix/common/prj.conf +++ b/tests/posix/common/prj.conf @@ -6,5 +6,6 @@ CONFIG_SEM_VALUE_MAX=32767 CONFIG_POSIX_MQUEUE=y CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_MAX_THREAD_BYTES=4 +CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=4096 CONFIG_SMP=n diff --git a/tests/posix/common/src/pthread.c b/tests/posix/common/src/pthread.c index 293eb038e0e7c5..d96c490a7b0ebb 100644 --- a/tests/posix/common/src/pthread.c +++ b/tests/posix/common/src/pthread.c @@ -11,18 +11,23 @@ #define N_THR_E 3 #define N_THR_T 4 +#define N_THR_T_AUTO_STACK 1 #define BOUNCES 64 #define STACKS (1024 + CONFIG_TEST_EXTRA_STACKSIZE) #define THREAD_PRIORITY 3 #define ONE_SECOND 1 +#if CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE < STACKS * N_THR_T_AUTO_STACK +#error "Insufficient malloc arena size" +#endif + /* Macros to test invalid states */ #define PTHREAD_CANCEL_INVALID -1 #define SCHED_INVALID -1 #define PRIO_INVALID -1 K_THREAD_STACK_ARRAY_DEFINE(stack_e, N_THR_E, STACKS); -K_THREAD_STACK_ARRAY_DEFINE(stack_t, N_THR_T, STACKS); +K_THREAD_STACK_ARRAY_DEFINE(stack_t, N_THR_T - N_THR_T_AUTO_STACK, STACKS); void *thread_top_exec(void *p1); void *thread_top_term(void *p1); @@ -386,7 +391,15 @@ void test_posix_pthread_termination(void) schedparam.sched_priority = 2; pthread_attr_setschedparam(&attr[i], &schedparam); - pthread_attr_setstack(&attr[i], &stack_t[i][0], STACKS); + + if (i < N_THR_T - N_THR_T_AUTO_STACK) { + pthread_attr_setstack(&attr[i], + &stack_t[i][0], + STACKS); + } else { + pthread_attr_setstacksize(&attr[i], STACKS); + } + ret = pthread_create(&newthread[i], &attr[i], thread_top_term, (void *)i);