From 74cc8a50dfd277bd5ef1a2093e7f42c9b9ea02f2 Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Fri, 8 Mar 2024 11:49:17 -0500 Subject: [PATCH 1/4] cpp: thread: support for std::thread and std::this_thread This commit adds support for std::thread and std::this_thread when the configured C++ standard is >= C++11. The implementation uses POSIX threads under the hood. Signed-off-by: Christopher Friedt --- lib/cpp/CMakeLists.txt | 2 +- lib/cpp/Kconfig | 8 ++++++++ lib/cpp/std/CMakeLists.txt | 3 +++ lib/cpp/std/thread.cpp | 29 +++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 lib/cpp/std/CMakeLists.txt create mode 100644 lib/cpp/std/thread.cpp diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt index 3ac7701daaea..72ec82ad87e2 100644 --- a/lib/cpp/CMakeLists.txt +++ b/lib/cpp/CMakeLists.txt @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 add_subdirectory(abi) - +add_subdirectory(std) add_subdirectory_ifdef(CONFIG_MINIMAL_LIBCPP minimal) diff --git a/lib/cpp/Kconfig b/lib/cpp/Kconfig index 1b0f8ee6396c..bdaae9ff99f3 100644 --- a/lib/cpp/Kconfig +++ b/lib/cpp/Kconfig @@ -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 diff --git a/lib/cpp/std/CMakeLists.txt b/lib/cpp/std/CMakeLists.txt new file mode 100644 index 000000000000..3bf8027dc7c6 --- /dev/null +++ b/lib/cpp/std/CMakeLists.txt @@ -0,0 +1,3 @@ +zephyr_sources_ifdef(CONFIG_CPP_STD_THREAD + thread.cpp +) diff --git a/lib/cpp/std/thread.cpp b/lib/cpp/std/thread.cpp new file mode 100644 index 000000000000..f42f1d0b6ee4 --- /dev/null +++ b/lib/cpp/std/thread.cpp @@ -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 + +#include + +#include +#include + +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); From 17ec7880596ca7de2282b2f1f13648d526bcc7dc Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Fri, 8 Mar 2024 11:49:17 -0500 Subject: [PATCH 2/4] tests: cpp: tests for std::thread and std::this_thread This commit adds tests for std::thread and std::this_thread. Signed-off-by: Christopher Friedt --- tests/lib/cpp/std/thread/CMakeLists.txt | 8 + tests/lib/cpp/std/thread/prj.conf | 15 ++ tests/lib/cpp/std/thread/src/this_thread.cpp | 41 +++++ tests/lib/cpp/std/thread/src/thread.cpp | 174 +++++++++++++++++++ tests/lib/cpp/std/thread/testcase.yaml | 25 +++ 5 files changed, 263 insertions(+) create mode 100644 tests/lib/cpp/std/thread/CMakeLists.txt create mode 100644 tests/lib/cpp/std/thread/prj.conf create mode 100644 tests/lib/cpp/std/thread/src/this_thread.cpp create mode 100644 tests/lib/cpp/std/thread/src/thread.cpp create mode 100644 tests/lib/cpp/std/thread/testcase.yaml diff --git a/tests/lib/cpp/std/thread/CMakeLists.txt b/tests/lib/cpp/std/thread/CMakeLists.txt new file mode 100644 index 000000000000..5cb0a56b86ba --- /dev/null +++ b/tests/lib/cpp/std/thread/CMakeLists.txt @@ -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(thread) + +FILE(GLOB_RECURSE app_sources src/*.cpp) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/lib/cpp/std/thread/prj.conf b/tests/lib/cpp/std/thread/prj.conf new file mode 100644 index 000000000000..13c1589c71f1 --- /dev/null +++ b/tests/lib/cpp/std/thread/prj.conf @@ -0,0 +1,15 @@ +CONFIG_ZTEST=y + +CONFIG_CPP=y +CONFIG_REQUIRES_FULL_LIBCPP=y +CONFIG_CPP_EXCEPTIONS=y +CONFIG_STD_CPP20=y + +CONFIG_COMMON_LIBC_THRD=y +CONFIG_POSIX_API=y +CONFIG_THREAD_STACK_INFO=y +CONFIG_DYNAMIC_THREAD=y +CONFIG_DYNAMIC_THREAD_POOL_SIZE=6 +CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096 + +CONFIG_ZTEST_STACK_SIZE=4096 diff --git a/tests/lib/cpp/std/thread/src/this_thread.cpp b/tests/lib/cpp/std/thread/src/this_thread.cpp new file mode 100644 index 000000000000..85d013bc5c00 --- /dev/null +++ b/tests/lib/cpp/std/thread/src/this_thread.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +using namespace std; +using namespace std::chrono_literals; + +static auto now() +{ + return chrono::steady_clock::now(); +} + +ZTEST(this_thread, test_get_id) +{ + thread::id id = this_thread::get_id(); +} + +ZTEST(this_thread, test_yield) +{ + this_thread::yield(); +} + +ZTEST(this_thread, test_sleep_until) +{ + this_thread::sleep_until(now() + 10ms); +} + +ZTEST(this_thread, test_sleep_for) +{ + this_thread::sleep_for(10ms); +} + +ZTEST_SUITE(this_thread, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/lib/cpp/std/thread/src/thread.cpp b/tests/lib/cpp/std/thread/src/thread.cpp new file mode 100644 index 000000000000..0924f6e31890 --- /dev/null +++ b/tests/lib/cpp/std/thread/src/thread.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2020 Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +using namespace std; + +ZTEST(thread, test_joinable) +{ + // implicitly tests the move assignment operator (operator=) + thread th = thread([]() {}); + zassert_true(th.joinable(), "non-default constructed thread should be joinable"); + +#ifdef __cpp_exceptions + try { + th.join(); + } catch (const system_error &e) { + zassert_true(false, "joinable thread should join"); + } +#else + th.join(); +#endif + + // This is an issue with gthread, so disabling the test for now. + if (false) { + zassert_false(th.joinable(), "previously joined thread should not be joinable"); + + th = std::thread([]() {}); + th.detach(); + zassert_false(th.joinable(), "detached thread should not be joinable"); + } +} + +ZTEST(thread, test_get_id) +{ + thread::id tid = this_thread::get_id(); +} + +ZTEST(thread, test_native_handle) +{ + thread th([]() {}); + th.native_handle(); + th.join(); +} + +ZTEST(thread, test_hardware_concurrency) +{ + if (IS_ENABLED(CONFIG_ARCH_POSIX)) { + zassert_true(thread::hardware_concurrency() >= 1, "actual: %u, expected: >= 1", + thread::hardware_concurrency()); + } else { + zassert_true(thread::hardware_concurrency() == 0 || + thread::hardware_concurrency() == CONFIG_MP_NUM_CPUS, + "actual: %u, expected: %u", thread::hardware_concurrency(), + CONFIG_MP_NUM_CPUS); + } +} + +ZTEST(thread, test_join) +{ + thread th; + __unused bool caught = false; + +#ifdef __cpp_exceptions + try { + th.join(); + } catch (const system_error &e) { + caught = true; + } + zassert_true(caught, "join of default-constructed thread should throw"); +#endif + + th = thread([]() {}); +#ifdef __cpp_exceptions + caught = false; + try { + th.join(); + } catch (const system_error &e) { + caught = true; + } + zassert_false(caught, "join() should not throw"); +#else + th.join(); +#endif + +#ifdef __cpp_exceptions + caught = false; + try { + th.join(); + } catch (const system_error &e) { + caught = true; + } + zassert_true(caught, "join should throw with already-joined thread"); +#endif +} + +ZTEST(thread, test_detach) +{ + thread th; + __unused bool caught = false; + +#ifdef __cpp_exceptions + // this is the behaviour in linux for detach() with an invalid thread object + caught = false; + try { + th.detach(); + } catch (const system_error &e) { + caught = true; + zassert_equal(e.code(), errc::invalid_argument, "expected errc::invalid_argument"); + } + zassert_true(caught, "detach should throw with default-constructed thread"); +#endif + + th = thread([]() {}); +#ifdef __cpp_exceptions + caught = false; + try { + th.detach(); + } catch (const system_error &e) { + caught = true; + } + zassert_false(caught, "detach on a valid thread should not throw"); +#else + th.detach(); +#endif + +#ifdef __cpp_exceptions + caught = false; + try { + th.detach(); + } catch (const system_error &e) { + caught = true; + } + zassert_true(caught, "detach on an already-detached thread should throw"); +#endif +} + +ZTEST(thread, test_swap) +{ + thread th1; + thread th2; + +#ifdef __cpp_exceptions + bool caught = false; + try { + th1.swap(th2); + } catch (...) { + caught = true; + } + zassert_false(caught, "thread::swap() is noexcept"); +#endif + + th1 = thread([]() {}); + th2 = thread([]() {}); + + thread::id th1_id = th1.get_id(); + thread::id th2_id = th2.get_id(); + + th1.swap(th2); + + zassert_equal(th2.get_id(), th1_id, "expected ids to be swapped"); + zassert_equal(th1.get_id(), th2_id, "expected ids to be swapped"); + + th1.join(); + th2.join(); +} + +ZTEST_SUITE(thread, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/lib/cpp/std/thread/testcase.yaml b/tests/lib/cpp/std/thread/testcase.yaml new file mode 100644 index 000000000000..8bade16b920b --- /dev/null +++ b/tests/lib/cpp/std/thread/testcase.yaml @@ -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.thread.newlib.except: + tags: newlib + filter: CONFIG_NEWLIB_LIBC_SUPPORTED + extra_configs: + - CONFIG_NEWLIB_LIBC=y + - CONFIG_CPP_EXCEPTIONS=y + cpp.thread.newlib.noexcept: + tags: newlib + filter: CONFIG_NEWLIB_LIBC_SUPPORTED + extra_configs: + - CONFIG_NEWLIB_LIBC=y From 1b3ba575f528084c6830cc718e4058b77b32fcff Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Fri, 8 Mar 2024 11:49:17 -0500 Subject: [PATCH 3/4] tests: lib: cpp: add tests for std::condition_variable Add tests for the ISO C++11 std::condition_variable. Signed-off-by: Christopher Friedt --- .../cpp/std/condition_variable/CMakeLists.txt | 8 + tests/lib/cpp/std/condition_variable/prj.conf | 14 ++ .../cpp/std/condition_variable/src/main.cpp | 188 ++++++++++++++++++ .../cpp/std/condition_variable/testcase.yaml | 25 +++ 4 files changed, 235 insertions(+) create mode 100644 tests/lib/cpp/std/condition_variable/CMakeLists.txt create mode 100644 tests/lib/cpp/std/condition_variable/prj.conf create mode 100644 tests/lib/cpp/std/condition_variable/src/main.cpp create mode 100644 tests/lib/cpp/std/condition_variable/testcase.yaml diff --git a/tests/lib/cpp/std/condition_variable/CMakeLists.txt b/tests/lib/cpp/std/condition_variable/CMakeLists.txt new file mode 100644 index 000000000000..162a842719ad --- /dev/null +++ b/tests/lib/cpp/std/condition_variable/CMakeLists.txt @@ -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}) diff --git a/tests/lib/cpp/std/condition_variable/prj.conf b/tests/lib/cpp/std/condition_variable/prj.conf new file mode 100644 index 000000000000..8e63ae25b33d --- /dev/null +++ b/tests/lib/cpp/std/condition_variable/prj.conf @@ -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 \ No newline at end of file diff --git a/tests/lib/cpp/std/condition_variable/src/main.cpp b/tests/lib/cpp/std/condition_variable/src/main.cpp new file mode 100644 index 000000000000..3a40080927da --- /dev/null +++ b/tests/lib/cpp/std/condition_variable/src/main.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2020, Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include + +constexpr auto N = CONFIG_DYNAMIC_THREAD_POOL_SIZE - 1; + +using namespace std::chrono_literals; + +using thread_pool = std::array; +using scoped_lock = std::unique_lock; +using time_point = std::chrono::time_point; + +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); diff --git a/tests/lib/cpp/std/condition_variable/testcase.yaml b/tests/lib/cpp/std/condition_variable/testcase.yaml new file mode 100644 index 000000000000..afa603803c13 --- /dev/null +++ b/tests/lib/cpp/std/condition_variable/testcase.yaml @@ -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 From 984a65baf8499d44059374bf879e2c475ecaa2e2 Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Fri, 8 Mar 2024 11:49:17 -0500 Subject: [PATCH 4/4] tests: lib: cpp: add tests for std::mutex and friends Add tests for ISO C++ std::mutex. Signed-off-by: Christopher Friedt --- tests/lib/cpp/std/mutex/CMakeLists.txt | 8 ++++ tests/lib/cpp/std/mutex/prj.conf | 14 +++++++ tests/lib/cpp/std/mutex/src/_main.cpp | 35 ++++++++++++++++ tests/lib/cpp/std/mutex/src/_main.hpp | 20 ++++++++++ tests/lib/cpp/std/mutex/src/plain.cpp | 30 ++++++++++++++ tests/lib/cpp/std/mutex/src/recursive.cpp | 35 ++++++++++++++++ .../lib/cpp/std/mutex/src/recursive_timed.cpp | 40 +++++++++++++++++++ tests/lib/cpp/std/mutex/src/timed.cpp | 34 ++++++++++++++++ tests/lib/cpp/std/mutex/testcase.yaml | 25 ++++++++++++ 9 files changed, 241 insertions(+) create mode 100644 tests/lib/cpp/std/mutex/CMakeLists.txt create mode 100644 tests/lib/cpp/std/mutex/prj.conf create mode 100644 tests/lib/cpp/std/mutex/src/_main.cpp create mode 100644 tests/lib/cpp/std/mutex/src/_main.hpp create mode 100644 tests/lib/cpp/std/mutex/src/plain.cpp create mode 100644 tests/lib/cpp/std/mutex/src/recursive.cpp create mode 100644 tests/lib/cpp/std/mutex/src/recursive_timed.cpp create mode 100644 tests/lib/cpp/std/mutex/src/timed.cpp create mode 100644 tests/lib/cpp/std/mutex/testcase.yaml diff --git a/tests/lib/cpp/std/mutex/CMakeLists.txt b/tests/lib/cpp/std/mutex/CMakeLists.txt new file mode 100644 index 000000000000..d7eced7aa6ba --- /dev/null +++ b/tests/lib/cpp/std/mutex/CMakeLists.txt @@ -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}) diff --git a/tests/lib/cpp/std/mutex/prj.conf b/tests/lib/cpp/std/mutex/prj.conf new file mode 100644 index 000000000000..0d0c789d634a --- /dev/null +++ b/tests/lib/cpp/std/mutex/prj.conf @@ -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 diff --git a/tests/lib/cpp/std/mutex/src/_main.cpp b/tests/lib/cpp/std/mutex/src/_main.cpp new file mode 100644 index 000000000000..308616b4b882 --- /dev/null +++ b/tests/lib/cpp/std/mutex/src/_main.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020, Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "_main.hpp" + +#include + +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); diff --git a/tests/lib/cpp/std/mutex/src/_main.hpp b/tests/lib/cpp/std/mutex/src/_main.hpp new file mode 100644 index 000000000000..8eef4d973b11 --- /dev/null +++ b/tests/lib/cpp/std/mutex/src/_main.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020, Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +using namespace std::chrono_literals; + +using time_point = std::chrono::time_point; + +time_point now(); +void time_init(); + +extern bool lock_succeeded; +extern time_point t0, t1, t2, t3; +constexpr auto dt = 100ms; diff --git a/tests/lib/cpp/std/mutex/src/plain.cpp b/tests/lib/cpp/std/mutex/src/plain.cpp new file mode 100644 index 000000000000..e75776ed75ce --- /dev/null +++ b/tests/lib/cpp/std/mutex/src/plain.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020, Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "_main.hpp" + +#include + +static std::mutex mu; + +ZTEST(std_mutex, test_plain) +{ + mu.lock(); + + std::thread th([] { + zassert_false(mu.try_lock()); + mu.lock(); + lock_succeeded = true; + mu.unlock(); + }); + std::this_thread::sleep_until(t1); + zassert_false(lock_succeeded); + mu.unlock(); + + th.join(); + + zassert_true(lock_succeeded); +} diff --git a/tests/lib/cpp/std/mutex/src/recursive.cpp b/tests/lib/cpp/std/mutex/src/recursive.cpp new file mode 100644 index 000000000000..02cd4bdb3071 --- /dev/null +++ b/tests/lib/cpp/std/mutex/src/recursive.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020, Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "_main.hpp" + +#include + +static std::recursive_mutex mu; + +ZTEST(std_mutex, test_recursive) +{ + mu.lock(); + zassert_true(mu.try_lock()); + + std::thread th([] { + zassert_false(mu.try_lock()); + std::this_thread::sleep_until(t2); + mu.lock(); + mu.lock(); + lock_succeeded = true; + mu.unlock(); + mu.unlock(); + }); + std::this_thread::sleep_until(t1); + zassert_false(lock_succeeded); + mu.unlock(); + mu.unlock(); + + th.join(); + + zassert_true(lock_succeeded); +} diff --git a/tests/lib/cpp/std/mutex/src/recursive_timed.cpp b/tests/lib/cpp/std/mutex/src/recursive_timed.cpp new file mode 100644 index 000000000000..d858661a987b --- /dev/null +++ b/tests/lib/cpp/std/mutex/src/recursive_timed.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020, Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "_main.hpp" + +#include + +static std::recursive_timed_mutex mu; + +ZTEST(std_mutex, test_resursive_timed) +{ + mu.lock(); + zassert_true(mu.try_lock()); + + std::thread th([] { + zassert_false(mu.try_lock()); + zassert_false(mu.try_lock_for(2 * dt)); + mu.lock(); + mu.lock(); + lock_succeeded = true; + mu.unlock(); + mu.unlock(); + }); + std::this_thread::sleep_until(t1); + zassert_false(lock_succeeded); + mu.unlock(); + mu.unlock(); + + zassert_true(mu.try_lock_until(t3)); + mu.unlock(); + + th.join(); + + zassert_true(lock_succeeded); +} diff --git a/tests/lib/cpp/std/mutex/src/timed.cpp b/tests/lib/cpp/std/mutex/src/timed.cpp new file mode 100644 index 000000000000..1efe76561e32 --- /dev/null +++ b/tests/lib/cpp/std/mutex/src/timed.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020, Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "_main.hpp" + +#include + +static std::timed_mutex mu; + +ZTEST(std_mutex, test_timed) +{ + mu.lock(); + + std::thread th([] { + zassert_false(mu.try_lock()); + zassert_false(mu.try_lock_for(2 * dt)); + mu.lock(); + lock_succeeded = true; + mu.unlock(); + }); + std::this_thread::sleep_until(t1); + zassert_false(lock_succeeded); + mu.unlock(); + + zassert_true(mu.try_lock_until(t3)); + mu.unlock(); + + th.join(); + + zassert_true(lock_succeeded); +} diff --git a/tests/lib/cpp/std/mutex/testcase.yaml b/tests/lib/cpp/std/mutex/testcase.yaml new file mode 100644 index 000000000000..6d5505b710b3 --- /dev/null +++ b/tests/lib/cpp/std/mutex/testcase.yaml @@ -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.mutex.newlib.except: + tags: newlib + filter: CONFIG_NEWLIB_LIBC_SUPPORTED + extra_configs: + - CONFIG_NEWLIB_LIBC=y + - CONFIG_CPP_EXCEPTIONS=y + cpp.mutex.newlib.noexcept: + tags: newlib + filter: CONFIG_NEWLIB_LIBC_SUPPORTED + extra_configs: + - CONFIG_NEWLIB_LIBC=y