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
37 changes: 37 additions & 0 deletions include/zephyr/posix/sys/times.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2025 Tenstorrent AI ULC
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_POSIX_SYS_TIMES_H_
#define ZEPHYR_INCLUDE_POSIX_SYS_TIMES_H_

#include <time.h>

#ifdef __cplusplus
extern "C" {
#endif

#if defined(_POSIX_MULTI_PROCESS) || defined(__DOXYGEN__)

#if !defined(_TMS_DECLARED) && !defined(__tms_defined)
struct tms {
clock_t tms_utime;
clock_t tms_stime;
clock_t tms_cutime;
clock_t tms_cstime;
};
#define _TMS_DECLARED
#define __tms_defined
#endif

clock_t times(struct tms *buf);

#endif /* _POSIX_MULTI_PROCESS */

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_POSIX_SYS_TIMES_H_ */
3 changes: 3 additions & 0 deletions lib/posix/options/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ if (NOT CONFIG_TC_PROVIDES_POSIX_MULTI_PROCESS)
multi_process.c
)
endif()
if (CONFIG_POSIX_MULTI_PROCESS)
zephyr_compile_definitions(-D_POSIX_MULTI_PROCESS=${POSIX_VERSION})
endif()

if (NOT CONFIG_TC_PROVIDES_POSIX_NETWORKING)
zephyr_library_sources_ifdef(CONFIG_POSIX_NETWORKING net.c)
Expand Down
2 changes: 2 additions & 0 deletions lib/posix/options/Kconfig.procN
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

menuconfig POSIX_MULTI_PROCESS
bool "POSIX multi-process support"
select SCHED_THREAD_USAGE # times()
select THREAD_RUNTIME_STATS
help
Support for multi-processing.

Expand Down
34 changes: 34 additions & 0 deletions lib/posix/options/multi_process.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <time.h>

#include <zephyr/kernel.h>
#include <zephyr/posix/sys/times.h>
#include <zephyr/posix/unistd.h>
#include <zephyr/sys/clock.h>
#include <zephyr/sys/time_units.h>
#include <zephyr/sys/util.h>

pid_t getpid(void)
{
Expand All @@ -24,3 +32,29 @@ pid_t getpid(void)
#ifdef CONFIG_POSIX_MULTI_PROCESS_ALIAS_GETPID
FUNC_ALIAS(getpid, _getpid, pid_t);
#endif /* CONFIG_POSIX_MULTI_PROCESS_ALIAS_GETPID */

clock_t times(struct tms *buffer)
{
int ret;
clock_t utime; /* user time */
k_thread_runtime_stats_t stats;

ret = k_thread_runtime_stats_all_get(&stats);
if (ret < 0) {
errno = -ret;
return (clock_t)-1;
}

utime = z_tmcvt(stats.total_cycles, sys_clock_hw_cycles_per_sec(), USEC_PER_SEC,
IS_ENABLED(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME) ? false : true,
sizeof(clock_t) == sizeof(uint32_t), false, false);

*buffer = (struct tms){
.tms_utime = utime,
.tms_stime = 0,
.tms_cutime = 0,
.tms_cstime = 0,
};

return utime;
}
10 changes: 10 additions & 0 deletions tests/posix/multi_process/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(posix_multi_process)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

target_compile_options(app PRIVATE -U_POSIX_C_SOURCE -D_POSIX_C_SOURCE=200809L)
5 changes: 5 additions & 0 deletions tests/posix/multi_process/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CONFIG_POSIX_API=y
CONFIG_ZTEST=y

CONFIG_POSIX_AEP_CHOICE_BASE=y
CONFIG_POSIX_MULTI_PROCESS=y
9 changes: 9 additions & 0 deletions tests/posix/multi_process/src/_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright (c) 2025 Tenstorrent AI ULC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/ztest.h>

ZTEST_SUITE(posix_multi_process, NULL, NULL, NULL, NULL, NULL);
16 changes: 16 additions & 0 deletions tests/posix/multi_process/src/getpid.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2025 Tenstorrent AI ULC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <unistd.h>

#include <zephyr/ztest.h>

ZTEST(posix_multi_process, test_getpid)
{
pid_t pid = getpid();

zexpect_true(pid > 0, "invalid pid: %d", pid);
}
56 changes: 56 additions & 0 deletions tests/posix/multi_process/src/sleep.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2022, Meta
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <unistd.h>

#include <zephyr/ztest.h>

struct waker_work {
k_tid_t tid;
struct k_work_delayable dwork;
};
static struct waker_work wake_work;

static void waker_func(struct k_work *work)
{
struct waker_work *ww;
struct k_work_delayable *dwork = k_work_delayable_from_work(work);

ww = CONTAINER_OF(dwork, struct waker_work, dwork);
k_wakeup(ww->tid);
}
K_WORK_DELAYABLE_DEFINE(waker, waker_func);

ZTEST(posix_multi_process, test_sleep)
{
uint32_t then;
uint32_t now;
/* call sleep(10), wakeup after 1s, expect >= 8s left */
const uint32_t sleep_min_s = 1;
const uint32_t sleep_max_s = 10;
const uint32_t sleep_rem_s = 8;

/* sleeping for 0s should return 0 */
zassert_ok(sleep(0));

/* test that sleeping for 1s sleeps for at least 1s */
then = k_uptime_get();
zassert_equal(0, sleep(1));
now = k_uptime_get();
zassert_true((now - then) >= 1 * MSEC_PER_SEC);

/* test that sleeping for 2s sleeps for at least 2s */
then = k_uptime_get();
zassert_equal(0, sleep(2));
now = k_uptime_get();
zassert_true((now - then) >= 2 * MSEC_PER_SEC);

/* test that sleep reports the remainder */
wake_work.tid = k_current_get();
k_work_init_delayable(&wake_work.dwork, waker_func);
zassert_equal(1, k_work_schedule(&wake_work.dwork, K_SECONDS(sleep_min_s)));
zassert_true(sleep(sleep_max_s) >= sleep_rem_s);
}
63 changes: 63 additions & 0 deletions tests/posix/multi_process/src/times.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2025 Tenstorrent AI ULC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <sys/times.h>

#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include <zephyr/ztest.h>

ZTEST(posix_multi_process, test_times)
{
static const struct {
const char *name;
size_t offset;
} fields[] = {
{
.name = "utime",
.offset = offsetof(struct tms, tms_utime),
},
{
.name = "stime",
.offset = offsetof(struct tms, tms_stime),
},
{
.name = "cutime",
.offset = offsetof(struct tms, tms_cutime),
},
{
.name = "cstime",
.offset = offsetof(struct tms, tms_cstime),
},
};
struct tms test_tms[2] = {};
clock_t rtime[2];

rtime[0] = times(&test_tms[0]);
k_msleep(MSEC_PER_SEC);
rtime[1] = times(&test_tms[1]);

zexpect_not_equal(rtime[0], -1);
zexpect_not_equal(rtime[1], -1);

printk("t0: rtime: %ld utime: %ld stime: %ld cutime: %ld cstime: %ld\n", rtime[0],
test_tms[0].tms_utime, test_tms[0].tms_stime, test_tms[0].tms_cutime,
test_tms[0].tms_cstime);
printk("t1: rtime: %ld utime: %ld stime: %ld cutime: %ld cstime: %ld\n", rtime[1],
test_tms[1].tms_utime, test_tms[1].tms_stime, test_tms[1].tms_cutime,
test_tms[1].tms_cstime);

ARRAY_FOR_EACH(fields, i) {
const char *name = fields[i].name;
size_t offset = fields[i].offset;

clock_t t0 = *(clock_t *)((uint8_t *)&test_tms[0] + offset);
clock_t t1 = *(clock_t *)((uint8_t *)&test_tms[1] + offset);

zexpect_true(t1 >= t0, "time moved backward for tms_%s: t0: %d t1: %d", name, t0,
t1);
}
}
27 changes: 27 additions & 0 deletions tests/posix/multi_process/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
common:
filter: not CONFIG_NATIVE_LIBC
tags:
- posix
- multi_process
# 1 tier0 platform per supported architecture
platform_key:
- arch
- simulation
integration_platforms:
- qemu_riscv64
min_flash: 64
min_ram: 32
tests:
portability.posix.muti_process: {}
portability.posix.muti_process.minimal:
extra_configs:
- CONFIG_MINIMAL_LIBC=y
portability.posix.muti_process.newlib:
filter: TOOLCHAIN_HAS_NEWLIB == 1
extra_configs:
- CONFIG_NEWLIB_LIBC=y
portability.posix.muti_process.picolibc:
tags: picolibc
filter: CONFIG_PICOLIBC_SUPPORTED
extra_configs:
- CONFIG_PICOLIBC=y
31 changes: 0 additions & 31 deletions tests/posix/timers/src/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,6 @@ static void waker_func(struct k_work *work)
}
K_WORK_DELAYABLE_DEFINE(waker, waker_func);

ZTEST(posix_timers, test_sleep)
{
uint32_t then;
uint32_t now;
/* call sleep(10), wakeup after 1s, expect >= 8s left */
const uint32_t sleep_min_s = 1;
const uint32_t sleep_max_s = 10;
const uint32_t sleep_rem_s = 8;

/* sleeping for 0s should return 0 */
zassert_ok(sleep(0));

/* test that sleeping for 1s sleeps for at least 1s */
then = k_uptime_get();
zassert_equal(0, sleep(1));
now = k_uptime_get();
zassert_true((now - then) >= 1 * MSEC_PER_SEC);

/* test that sleeping for 2s sleeps for at least 2s */
then = k_uptime_get();
zassert_equal(0, sleep(2));
now = k_uptime_get();
zassert_true((now - then) >= 2 * MSEC_PER_SEC);

/* test that sleep reports the remainder */
wake_work.tid = k_current_get();
k_work_init_delayable(&wake_work.dwork, waker_func);
zassert_equal(1, k_work_schedule(&wake_work.dwork, K_SECONDS(sleep_min_s)));
zassert_true(sleep(sleep_max_s) >= sleep_rem_s);
}

ZTEST(posix_timers, test_usleep)
{
uint32_t then;
Expand Down