Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/zephyr/posix/pthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@ int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_once(pthread_once_t *once, void (*initFunc)(void));
#endif
FUNC_NORETURN void pthread_exit(void *retval);
int pthread_timedjoin_np(pthread_t thread, void **status, const struct timespec *abstime);
int pthread_tryjoin_np(pthread_t thread, void **status);
int pthread_join(pthread_t thread, void **status);
int pthread_cancel(pthread_t pthread);
int pthread_detach(pthread_t thread);
Expand Down
61 changes: 53 additions & 8 deletions lib/posix/options/pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ BUILD_ASSERT((PTHREAD_CANCEL_ENABLE == 0 || PTHREAD_CANCEL_DISABLE == 0) &&
BUILD_ASSERT(CONFIG_POSIX_PTHREAD_ATTR_STACKSIZE_BITS + CONFIG_POSIX_PTHREAD_ATTR_GUARDSIZE_BITS <=
32);

int64_t timespec_to_timeoutms(const struct timespec *abstime);
static void posix_thread_recycle(void);
static sys_dlist_t posix_thread_q[] = {
SYS_DLIST_STATIC_INIT(&posix_thread_q[POSIX_THREAD_READY_Q]),
Expand Down Expand Up @@ -1061,12 +1062,7 @@ void pthread_exit(void *retval)
CODE_UNREACHABLE;
}

/**
* @brief Wait for a thread termination.
*
* See IEEE 1003.1
*/
int pthread_join(pthread_t pthread, void **status)
static int pthread_timedjoin_internal(pthread_t pthread, void **status, k_timeout_t timeout)
{
int ret = ESRCH;
struct posix_thread *t = NULL;
Expand Down Expand Up @@ -1115,8 +1111,19 @@ int pthread_join(pthread_t pthread, void **status)
break;
}

ret = k_thread_join(&t->thread, K_FOREVER);
/* other possibilities? */
ret = k_thread_join(&t->thread, timeout);
if (ret != 0) {
/* when joining failed, ensure that the thread can be joined later */
SYS_SEM_LOCK(&pthread_pool_lock) {
t->attr.detachstate = PTHREAD_CREATE_JOINABLE;
}
}
if (ret == -EBUSY) {
return EBUSY;
} else if (ret == -EAGAIN) {
return ETIMEDOUT;
}
/* Can only be ok or -EDEADLK, which should never occur for pthreads */
__ASSERT_NO_MSG(ret == 0);

LOG_DBG("Joined pthread %p", &t->thread);
Expand All @@ -1131,6 +1138,44 @@ int pthread_join(pthread_t pthread, void **status)
return 0;
}

/**
* @brief Await a thread termination with timeout.
*
* Non-portable GNU extension of IEEE 1003.1
*/
int pthread_timedjoin_np(pthread_t pthread, void **status, const struct timespec *abstime)
{
if (abstime == NULL) {
return EINVAL;
}

if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= NSEC_PER_SEC) {
return EINVAL;
}

return pthread_timedjoin_internal(pthread, status, K_MSEC(timespec_to_timeoutms(abstime)));
}

/**
* @brief Check a thread for termination.
*
* Non-portable GNU extension of IEEE 1003.1
*/
int pthread_tryjoin_np(pthread_t pthread, void **status)
{
return pthread_timedjoin_internal(pthread, status, K_NO_WAIT);
}

/**
* @brief Await a thread termination.
*
* See IEEE 1003.1
*/
int pthread_join(pthread_t pthread, void **status)
{
return pthread_timedjoin_internal(pthread, status, K_FOREVER);
}

/**
* @brief Detach a thread.
*
Expand Down
71 changes: 71 additions & 0 deletions tests/posix/common/src/pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ static void *thread_top_exec(void *p1)
return NULL;
}

static void *timedjoin_thread(void *p1)
{
int sleep_duration_ms = POINTER_TO_INT(p1);

usleep(USEC_PER_MSEC * sleep_duration_ms);
return NULL;
}

static int bounce_test_done(void)
{
int i;
Expand Down Expand Up @@ -371,6 +379,69 @@ ZTEST(pthread, test_pthread_termination)
zassert_equal(ret, ESRCH, "cancelled a terminated thread!");
}

ZTEST(pthread, test_pthread_tryjoin)
{
pthread_t th = {0};
int sleep_duration_ms = 200;
void *retval;

/* Creating a thread that exits after 200ms*/
zassert_ok(pthread_create(&th, NULL, timedjoin_thread, INT_TO_POINTER(sleep_duration_ms)));

/* Attempting to join, when thread is still running, should fail */
usleep(USEC_PER_MSEC * sleep_duration_ms / 2);
zassert_equal(pthread_tryjoin_np(th, &retval), EBUSY);

/* Sleep so thread will exit */
usleep(USEC_PER_MSEC * sleep_duration_ms);

/* Attempting to join without blocking should succeed now */
zassert_ok(pthread_tryjoin_np(th, &retval));
}

ZTEST(pthread, test_pthread_timedjoin)
{
pthread_t th = {0};
int sleep_duration_ms = 200;
void *ret;
struct timespec not_done;
struct timespec done;
struct timespec invalid[] = {
[0] = {.tv_sec = -1},
[1] = {.tv_nsec = -1},
[2] = {.tv_nsec = NSEC_PER_SEC},
};

/* setup timespecs when the thread is still running and when it is done */
clock_gettime(CLOCK_MONOTONIC, &not_done);
clock_gettime(CLOCK_MONOTONIC, &done);
not_done.tv_nsec += sleep_duration_ms / 2 * NSEC_PER_MSEC;
done.tv_nsec += sleep_duration_ms * 1.5 * NSEC_PER_MSEC;
while (not_done.tv_nsec >= NSEC_PER_SEC) {
not_done.tv_sec++;
not_done.tv_nsec -= NSEC_PER_SEC;
}
while (done.tv_nsec >= NSEC_PER_SEC) {
done.tv_sec++;
done.tv_nsec -= NSEC_PER_SEC;
}

/* Creating a thread that exits after 200ms*/
zassert_ok(pthread_create(&th, NULL, timedjoin_thread, INT_TO_POINTER(sleep_duration_ms)));

/* pthread_timedjoin-np must return -EINVAL for invalid struct timespecs */
zassert_equal(pthread_timedjoin_np(th, &ret, NULL), EINVAL);
for (size_t i = 0; i < ARRAY_SIZE(invalid); ++i) {
zassert_equal(pthread_timedjoin_np(th, &ret, &invalid[i]), EINVAL);
}

/* Attempting to join with a timeout, when the thread is still running should fail */
zassert_equal(pthread_timedjoin_np(th, &ret, &not_done), ETIMEDOUT);

/* Attempting to join with a timeout, when the thread is done, should succeed */
zassert_ok(pthread_timedjoin_np(th, &ret, &done));
}

static void *create_thread1(void *p1)
{
/* do nothing */
Expand Down
Loading