Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpp: thread: support for std::thread, std::mutex, std::condition_variable, etc #43729

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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: 1 addition & 1 deletion lib/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Apache-2.0

add_subdirectory(abi)

add_subdirectory(std)
add_subdirectory_ifdef(CONFIG_MINIMAL_LIBCPP minimal)
8 changes: 8 additions & 0 deletions lib/cpp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ config CPP_RTTI

endif # !MINIMAL_LIBCPP

config CPP_STD_THREAD
bool "Support for std::thread"
default y if !STD_CPP98
depends on !STD_CPP98
select POSIX_API
help
This option enables support for std::thread and std::this_thread.

config CPP_STATIC_INIT_GNU
bool
select STATIC_INIT_GNU
Expand Down
3 changes: 3 additions & 0 deletions lib/cpp/std/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
zephyr_sources_ifdef(CONFIG_CPP_STD_THREAD
thread.cpp
)
29 changes: 29 additions & 0 deletions lib/cpp/std/thread.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#define _GLIBCXX_VISIBILITY(x)
#define _GLIBCXX_THREAD_ABI_COMPAT 1

#include <threads.h>

#include <thread>

#include <zephyr/init.h>
#include <zephyr/kernel.h>

static void __z_cpp_thread_terminate() FUNC_NORETURN;
static void __z_cpp_thread_terminate()
{
thrd_exit(0);
}

static int __z_cpp_thread_init(void)
{
/* use thrd_exit(0) instead of std::abort() to avoid kernel panic */
std::set_terminate(__z_cpp_thread_terminate);
return 0;
}
SYS_INIT(__z_cpp_thread_init, PRE_KERNEL_1, 0);
8 changes: 8 additions & 0 deletions tests/lib/cpp/std/condition_variable/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(condition_variable)

FILE(GLOB_RECURSE app_sources src/*.cpp)
target_sources(app PRIVATE ${app_sources})
14 changes: 14 additions & 0 deletions tests/lib/cpp/std/condition_variable/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CONFIG_ZTEST=y

CONFIG_CPP=y
CONFIG_REQUIRES_FULL_LIBCPP=y
CONFIG_CPP_EXCEPTIONS=y
CONFIG_STD_CPP20=y

CONFIG_POSIX_API=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_DYNAMIC_THREAD=y
CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096
CONFIG_DYNAMIC_THREAD_POOL_SIZE=6
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_ZTEST_STACK_SIZE=4096
188 changes: 188 additions & 0 deletions tests/lib/cpp/std/condition_variable/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2020, Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <array>
#include <chrono>
#include <condition_variable>
#include <thread>

#include <zephyr/ztest.h>

constexpr auto N = CONFIG_DYNAMIC_THREAD_POOL_SIZE - 1;

using namespace std::chrono_literals;

using thread_pool = std::array<std::thread, N>;
using scoped_lock = std::unique_lock<std::mutex>;
using time_point = std::chrono::time_point<std::chrono::steady_clock>;

static std::mutex m;
static std::condition_variable cv;

/* number of threads awoken before t1 */
static size_t count;
static time_point t0, t1, t2, t3;
constexpr auto dt = 25ms;

static time_point now()
{
return std::chrono::steady_clock::now();
}

static void time_init()
{
t0 = now();
t1 = t0 + 2 * dt;
t2 = t0 + 3 * dt;
t3 = t0 + 4 * dt;
}

static void notify_common(size_t n)
{
thread_pool tp;

count = 0;
time_init();

for (auto &t : tp) {
t = std::thread([] {
scoped_lock lk(m);

do {
cv.wait_until(lk, t3);
auto nw = now();
if (nw >= t1 && nw < t2) {
++count;
} else {
break;
}
} while (true);
});
}

cv.notify_all();
zassert_equal(count, 0, "expect: %zu actual: %zu", 0, count);

std::this_thread::sleep_until(t1);

if (n == 1) {
cv.notify_one();
} else {
cv.notify_all();
}

for (auto &t : tp) {
t.join();
}

zassert_equal(count, n, "expect: %zu actual: %zu", n, count);
}

ZTEST(condition_variable, test_notify_one)
{
/*
* Take the current time, t0. Several threads wait until time t2. If any thread wakes before
* t2, increase count and exit. Notify one thread at time t1 = t2/2. Join all threads.
* Verify that count == 1.
*/

notify_common(1);
}

ZTEST(condition_variable, test_notify_all)
{
/*
* Take the current time, t0. Several threads wait until time t2. If any thread wakes before
* t2, increase count and exit. Notify all threads at time t1 = t2/2. Join all threads.
* Verify that count == N - 1.
*/

notify_common(N);
}

ZTEST(condition_variable, test_wait)
{
count = 0;
time_init();

auto t = std::thread([] {
scoped_lock lk(m);

cv.wait(lk);
count++;
});

std::this_thread::sleep_until(t1);
cv.notify_one();

{
scoped_lock lk(m);

cv.wait(lk, [] { return count == 1; });
}

t.join();

zassert_equal(count, 1);
}

ZTEST(condition_variable, test_wait_for)
{
count = 0;

auto t = std::thread([] {
scoped_lock lk(m);

zassert_equal(cv.wait_for(lk, 0ms), std::cv_status::timeout);
lk.lock();
zassert_equal(cv.wait_for(lk, 3 * dt), std::cv_status::no_timeout);
count++;
});

std::this_thread::sleep_for(2 * dt);
cv.notify_one();

{
scoped_lock lk(m);

zassert_true(cv.wait_for(lk, dt, [] { return count == 1; }));
}

t.join();

zassert_equal(count, 1);
}

ZTEST(condition_variable, test_wait_until)
{
count = 0;
time_init();

auto t = std::thread([] {
scoped_lock lk(m);

zassert_equal(std::cv_status::timeout, cv.wait_until(lk, t0));
std::this_thread::sleep_until(t1);
count++;
});

{
scoped_lock lk(m);

zassert_true(cv.wait_until(lk, t2, [] { return count == 1; }));
}

t.join();

zassert_equal(count, 1);
}

ZTEST(condition_variable, test_native_handle)
{
zassert_not_null(cv.native_handle());
}

ZTEST_SUITE(condition_variable, NULL, NULL, NULL, NULL, NULL);
25 changes: 25 additions & 0 deletions tests/lib/cpp/std/condition_variable/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
common:
tags: cpp
filter: CONFIG_FULL_LIBCPP_SUPPORTED
integration_platforms:
- qemu_cortex_a53
- mps2/an385
- qemu_riscv32
- qemu_riscv64
- qemu_x86
- qemu_x86_64
# llvm currently excluded due to 'inttypes.h' file not found
toolchain_exclude:
- llvm
tests:
cpp.condition_variable.newlib.except:
tags: newlib
filter: CONFIG_NEWLIB_LIBC_SUPPORTED
extra_configs:
- CONFIG_NEWLIB_LIBC=y
- CONFIG_CPP_EXCEPTIONS=y
cpp.condition_variable.newlib.noexcept:
tags: newlib
filter: CONFIG_NEWLIB_LIBC_SUPPORTED
extra_configs:
- CONFIG_NEWLIB_LIBC=y
8 changes: 8 additions & 0 deletions tests/lib/cpp/std/mutex/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(mutex)

FILE(GLOB_RECURSE app_sources src/*.cpp)
target_sources(app PRIVATE ${app_sources})
14 changes: 14 additions & 0 deletions tests/lib/cpp/std/mutex/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CONFIG_ZTEST=y

CONFIG_CPP=y
CONFIG_REQUIRES_FULL_LIBCPP=y
CONFIG_CPP_EXCEPTIONS=y
CONFIG_STD_CPP20=y

CONFIG_POSIX_API=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_DYNAMIC_THREAD=y
CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096
CONFIG_DYNAMIC_THREAD_POOL_SIZE=6

CONFIG_ZTEST_STACK_SIZE=4096
35 changes: 35 additions & 0 deletions tests/lib/cpp/std/mutex/src/_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020, Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "_main.hpp"

#include <zephyr/ztest.h>

bool lock_succeeded;
time_point t0, t1, t2, t3;

time_point now()
{
return std::chrono::steady_clock::now();
}

void time_init()
{
t0 = now();
t1 = t0 + 2 * dt;
t2 = t0 + 3 * dt;
t3 = t0 + 4 * dt;
}

static void before(void *arg)
{
ARG_UNUSED(arg);

lock_succeeded = false;
time_init();
}

ZTEST_SUITE(std_mutex, nullptr, nullptr, before, nullptr, nullptr);
20 changes: 20 additions & 0 deletions tests/lib/cpp/std/mutex/src/_main.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2020, Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <chrono>
#include <mutex>
#include <thread>

using namespace std::chrono_literals;

using time_point = std::chrono::time_point<std::chrono::steady_clock>;

time_point now();
void time_init();

extern bool lock_succeeded;
extern time_point t0, t1, t2, t3;
constexpr auto dt = 100ms;
Loading
Loading