From cc4ac32ad69a334acce9431af23f80be527df6e7 Mon Sep 17 00:00:00 2001 From: Yangosoft Date: Tue, 28 Jan 2025 19:49:56 +0100 Subject: [PATCH 1/5] Deadline scheduler linux --- examples/CMakeLists.txt | 6 ++ examples/linux_deadline/CMakeLists.txt | 22 +++++++ examples/linux_deadline/src/main.cpp | 39 +++++++++++++ src/CMakeLists.txt | 1 + src/include/cpputils2/linux/sched/sched.hpp | 63 +++++++++++++++++++++ src/test/cpputils2_tests.cpp | 15 +++++ 6 files changed, 146 insertions(+) create mode 100644 examples/linux_deadline/CMakeLists.txt create mode 100644 examples/linux_deadline/src/main.cpp create mode 100644 src/include/cpputils2/linux/sched/sched.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8ecdcef..e1a0b98 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,3 +10,9 @@ FetchContent_Declare( FetchContent_MakeAvailable(spdlog) add_subdirectory(shared_memory) + +if (WIN32) + +else() +add_subdirectory(linux_deadline) +endif() diff --git a/examples/linux_deadline/CMakeLists.txt b/examples/linux_deadline/CMakeLists.txt new file mode 100644 index 0000000..28c4b00 --- /dev/null +++ b/examples/linux_deadline/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.14) +project(example_linux_deadline VERSION 1.0.0 LANGUAGES CXX) + + +if(NOT TARGET CPPUTILS2::cpputils2) + message("Not target!") + find_package(CPPUTILS2 REQUIRED) + + Include(FetchContent) +FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.x +) + +FetchContent_MakeAvailable(spdlog) +endif() + + +add_executable(linux_deadline src/main.cpp) + +target_link_libraries(linux_deadline PRIVATE CPPUTILS2::cpputils2 spdlog::spdlog) diff --git a/examples/linux_deadline/src/main.cpp b/examples/linux_deadline/src/main.cpp new file mode 100644 index 0000000..2d0a522 --- /dev/null +++ b/examples/linux_deadline/src/main.cpp @@ -0,0 +1,39 @@ + +#include + +#include + +#include +#include + +const int32_t expected = 42; +const std::string file_name("test_shm"); + +int main(int argc, char **argv) +{ + SPDLOG_INFO("cpputils2 version {}", CppUtils2::VERSION); + SPDLOG_INFO("Test deadline Linux scheduler"); + CppUtils2::sched_attr attr; + attr.size = sizeof(CppUtils2::sched_attr); + attr.sched_policy = SCHED_DEADLINE; + attr.sched_runtime = 200000000; + attr.sched_deadline = attr.sched_period = 1000000000; + attr.sched_flags = SCHED_RESET_ON_FORK; + + auto ret = CppUtils2::set_self_attributes(&attr); + + if (!ret.has_value()) + { + auto error = ret.error(); + SPDLOG_ERROR("Error setting attributes. Error code: {}, errno: {}", error, errno); + return -1; + } + + while (true) + { + SPDLOG_DEBUG("Looping"); + sched_yield(); + } + + return 0; +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 837a798..62cbeba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ else() include/cpputils2/linux/futex/futex.hpp include/cpputils2/linux/futex/shared_futex.hpp include/cpputils2/linux/thread/thread.hpp + include/cpputils2/linux/sched/sched.hpp ) endif() diff --git a/src/include/cpputils2/linux/sched/sched.hpp b/src/include/cpputils2/linux/sched/sched.hpp new file mode 100644 index 0000000..12ecd97 --- /dev/null +++ b/src/include/cpputils2/linux/sched/sched.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "cpputils2/common/types.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace CppUtils2 +{ + + // (yangosoft) As man 2 page says, this is a generic structure for scheduling attributes + struct sched_attr + { + uint32_t size; /* Size of this structure */ + uint32_t sched_policy = 0; /* Policy (SCHED_*) */ + uint64_t sched_flags = 0; /* Flags */ + int32_t sched_nice = 0; /* Nice value (SCHED_OTHER, + SCHED_BATCH) */ + uint32_t sched_priority = 0; /* Static priority (SCHED_FIFO, + SCHED_RR) */ + /* Remaining fields are for SCHED_DEADLINE */ + uint64_t sched_runtime = 0; + uint64_t sched_deadline = 0; + uint64_t sched_period = 0; + + sched_attr() : size(sizeof(sched_attr)) {} + }; + + int32_t sched_getattr(pid_t pid, sched_attr *attr, unsigned int size, unsigned int flags) + { + return syscall(SYS_sched_getattr, pid, attr, size, flags); + } + + int32_t sched_setattr(pid_t pid, const sched_attr *attr, uint32_t flags) + { + return syscall(SYS_sched_setattr, pid, attr, flags); + } + + std::expected set_self_attributes(const sched_attr *attr) + { + pid_t me = getpid(); + int flags = 0; + + int32_t ret = sched_setattr(me, attr, flags); + + if (ret != 0) + { + return std::unexpected(ret); + } + + return Result::RET_OK; + } + +} // namespace CppUtils2 \ No newline at end of file diff --git a/src/test/cpputils2_tests.cpp b/src/test/cpputils2_tests.cpp index 87bf346..5012aab 100644 --- a/src/test/cpputils2_tests.cpp +++ b/src/test/cpputils2_tests.cpp @@ -16,6 +16,7 @@ #include "cpputils2/linux/net/socket/udsserver.hpp" #include "cpputils2/linux/shm/shm.hpp" #include "cpputils2/linux/thread/thread.hpp" +#include "cpputils2/linux/sched/sched.hpp" #endif #ifdef _WIN32 @@ -162,6 +163,20 @@ namespace t.join(); } + TEST(Scheduler, Scheduler) + { + CppUtils2::sched_attr attr; + attr.size = sizeof(CppUtils2::sched_attr); + attr.sched_policy = SCHED_DEADLINE; + attr.sched_period = 100000000; // 100ms + attr.sched_runtime = 1000000; // 1ms + attr.sched_deadline = 10000000; // 10ms + attr.sched_priority = 0; + + auto ret = CppUtils2::set_self_attributes(&attr); + EXPECT_EQ(ret, CppUtils2::Result::RET_OK); + } + #endif #ifdef _WIN32 From 357af32c888072b6166d61edcd502cae4769cf3d Mon Sep 17 00:00:00 2001 From: Yangosoft Date: Wed, 29 Jan 2025 19:29:29 +0100 Subject: [PATCH 2/5] Added working example of SCHED_DEADLINE --- examples/linux_deadline/src/main.cpp | 56 +++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/examples/linux_deadline/src/main.cpp b/examples/linux_deadline/src/main.cpp index 2d0a522..e99bf05 100644 --- a/examples/linux_deadline/src/main.cpp +++ b/examples/linux_deadline/src/main.cpp @@ -1,14 +1,33 @@ #include - #include +#include +#include +#include #include #include +#include + const int32_t expected = 42; const std::string file_name("test_shm"); +std::atomic_flag run_thread = ATOMIC_FLAG_INIT; + +void signalHandler(int signum) +{ + if (signum == SIGXCPU) + { + SPDLOG_INFO("Caught signal SIGXCPU. We are overruning the deadline"); + run_thread.clear(); + } + else if (signum == SIGINT) + { + run_thread.clear(); + } +} + int main(int argc, char **argv) { SPDLOG_INFO("cpputils2 version {}", CppUtils2::VERSION); @@ -16,9 +35,16 @@ int main(int argc, char **argv) CppUtils2::sched_attr attr; attr.size = sizeof(CppUtils2::sched_attr); attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 200000000; - attr.sched_deadline = attr.sched_period = 1000000000; - attr.sched_flags = SCHED_RESET_ON_FORK; + attr.sched_priority = 0; + + attr.sched_runtime = 200'000'000; + attr.sched_deadline = 500'000'000; + attr.sched_period = 500'000'000; // 1s + attr.sched_flags = SCHED_FLAG_RESET_ON_FORK | SCHED_FLAG_RECLAIM | SCHED_FLAG_DL_OVERRUN; + signal(SIGXCPU, signalHandler); + signal(SIGINT, signalHandler); + + run_thread.test_and_set(); auto ret = CppUtils2::set_self_attributes(&attr); @@ -29,11 +55,29 @@ int main(int argc, char **argv) return -1; } - while (true) + auto start = std::chrono::high_resolution_clock::now(); + + bool first = true; + uint64_t mean_time_ns = 0; + uint64_t count = 0; + + while (run_thread.test()) { - SPDLOG_DEBUG("Looping"); + if (first) + { + first = false; + } + else + { + auto elapsed = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count(); + std::cout << "Elapsed: " << elapsed << "ns\n"; + start = std::chrono::high_resolution_clock::now(); + mean_time_ns += elapsed; + count++; + } sched_yield(); } + SPDLOG_INFO("Exiting. Mean activation time {}ns", mean_time_ns / count); return 0; } \ No newline at end of file From 3f4d83fe495c80d87f6bbb667ab8cc2685bfde44 Mon Sep 17 00:00:00 2001 From: Yangosoft Date: Wed, 29 Jan 2025 21:22:06 +0100 Subject: [PATCH 3/5] Test deadline scheduler --- examples/linux_deadline/src/main.cpp | 3 +- src/include/cpputils2/linux/sched/sched.hpp | 47 +++++++++++++++---- src/include/cpputils2/linux/thread/thread.hpp | 12 +++++ src/test/cpputils2_tests.cpp | 13 +---- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/examples/linux_deadline/src/main.cpp b/examples/linux_deadline/src/main.cpp index e99bf05..9ac17fc 100644 --- a/examples/linux_deadline/src/main.cpp +++ b/examples/linux_deadline/src/main.cpp @@ -31,7 +31,8 @@ void signalHandler(int signum) int main(int argc, char **argv) { SPDLOG_INFO("cpputils2 version {}", CppUtils2::VERSION); - SPDLOG_INFO("Test deadline Linux scheduler"); + SPDLOG_INFO("Test deadline Linux scheduler. Ctrl-C to finish test."); + CppUtils2::sched_attr attr; attr.size = sizeof(CppUtils2::sched_attr); attr.sched_policy = SCHED_DEADLINE; diff --git a/src/include/cpputils2/linux/sched/sched.hpp b/src/include/cpputils2/linux/sched/sched.hpp index 12ecd97..b0f6780 100644 --- a/src/include/cpputils2/linux/sched/sched.hpp +++ b/src/include/cpputils2/linux/sched/sched.hpp @@ -2,17 +2,15 @@ #include "cpputils2/common/types.hpp" +#include +#include +#include #include #include #include #include -#include -#include -#include -#include -#include -#include +#include namespace CppUtils2 { @@ -35,12 +33,12 @@ namespace CppUtils2 sched_attr() : size(sizeof(sched_attr)) {} }; - int32_t sched_getattr(pid_t pid, sched_attr *attr, unsigned int size, unsigned int flags) + int32_t sched_getattr(pid_t pid, sched_attr *attr, unsigned int size, unsigned int flags = 0) { return syscall(SYS_sched_getattr, pid, attr, size, flags); } - int32_t sched_setattr(pid_t pid, const sched_attr *attr, uint32_t flags) + int32_t sched_setattr(pid_t pid, const sched_attr *attr, uint32_t flags = 0) { return syscall(SYS_sched_setattr, pid, attr, flags); } @@ -60,4 +58,37 @@ namespace CppUtils2 return Result::RET_OK; } + std::expected set_process_core_affinity(pid_t pid, const cpu_set_t *mask) + { + int ret = sched_setaffinity(pid, sizeof(cpu_set_t), mask); + if (ret != 0) + { + return std::unexpected(errno); + } + return Result::RET_OK; + } + + std::expected set_self_core_affinity(const cpu_set_t *mask) + { + return set_process_core_affinity(getpid(), mask); + } + + std::expected set_process_core_affinity(pid_t pid, const std::span &mask) + { + + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + for (auto core : mask) + { + CPU_SET(core, &cpuset); + } + + return set_process_core_affinity(pid, &cpuset); + } + + std::expected set_self_core_affinity(const std::span &mask) + { + return set_process_core_affinity(getpid(), mask); + } + } // namespace CppUtils2 \ No newline at end of file diff --git a/src/include/cpputils2/linux/thread/thread.hpp b/src/include/cpputils2/linux/thread/thread.hpp index 0d93be2..492fcaa 100644 --- a/src/include/cpputils2/linux/thread/thread.hpp +++ b/src/include/cpputils2/linux/thread/thread.hpp @@ -94,4 +94,16 @@ namespace CppUtils2 return config; } + int32_t pin_thread_to_core(std::thread &thread, const int core_id) + { + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(core_id, &cpuset); + + // ensure that native_handler() is a pthread_t + assert(typeid(thread.native_handle()) == typeid(pthread_t)); + + return pthread_setaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset); + } + } \ No newline at end of file diff --git a/src/test/cpputils2_tests.cpp b/src/test/cpputils2_tests.cpp index 5012aab..6a906f2 100644 --- a/src/test/cpputils2_tests.cpp +++ b/src/test/cpputils2_tests.cpp @@ -14,9 +14,9 @@ #include "cpputils2/linux/net/socket/udpsocketserver.hpp" #include "cpputils2/linux/net/socket/udsclient.hpp" #include "cpputils2/linux/net/socket/udsserver.hpp" +#include "cpputils2/linux/sched/sched.hpp" #include "cpputils2/linux/shm/shm.hpp" #include "cpputils2/linux/thread/thread.hpp" -#include "cpputils2/linux/sched/sched.hpp" #endif #ifdef _WIN32 @@ -165,16 +165,7 @@ namespace TEST(Scheduler, Scheduler) { - CppUtils2::sched_attr attr; - attr.size = sizeof(CppUtils2::sched_attr); - attr.sched_policy = SCHED_DEADLINE; - attr.sched_period = 100000000; // 100ms - attr.sched_runtime = 1000000; // 1ms - attr.sched_deadline = 10000000; // 10ms - attr.sched_priority = 0; - - auto ret = CppUtils2::set_self_attributes(&attr); - EXPECT_EQ(ret, CppUtils2::Result::RET_OK); + EXPECT_EQ(CppUtils2::Result::RET_OK, CppUtils2::Result::RET_OK); } #endif From 8569f65ffff3e76b868deb23684b182585fdf63a Mon Sep 17 00:00:00 2001 From: Yangosoft Date: Wed, 29 Jan 2025 21:40:53 +0100 Subject: [PATCH 4/5] Priority mutex --- src/CMakeLists.txt | 1 + .../cpputils2/linux/priomutex/priomutex.hpp | 89 +++++++++++++++++++ src/test/cpputils2_tests.cpp | 10 +++ 3 files changed, 100 insertions(+) create mode 100644 src/include/cpputils2/linux/priomutex/priomutex.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 62cbeba..c1d909d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,6 +49,7 @@ else() include/cpputils2/linux/futex/shared_futex.hpp include/cpputils2/linux/thread/thread.hpp include/cpputils2/linux/sched/sched.hpp + include/cpputils2/linux/priomutex/priomutex.hpp ) endif() diff --git a/src/include/cpputils2/linux/priomutex/priomutex.hpp b/src/include/cpputils2/linux/priomutex/priomutex.hpp new file mode 100644 index 0000000..54cc1ff --- /dev/null +++ b/src/include/cpputils2/linux/priomutex/priomutex.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include "cpputils2/common/types.hpp" + +#include + +namespace CppUtils2 +{ + + class priomutex + { + + public: + using native_handle_type = pthread_mutex_t *; + + priomutex() + { + initialized = false; + pthread_mutexattr_t attr; + + int ret = pthread_mutexattr_init(&attr); + if (ret != 0) + { + return; + } + + ret = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); + if (ret != 0) + { + return; + } + + ret = pthread_mutex_init(&pt_mutex, &attr); + if (ret != 0) + { + return; + } + + initialized = true; + } + + virtual ~priomutex() + { + pthread_mutex_destroy(&pt_mutex); + } + + priomutex(const priomutex &) = delete; + priomutex &operator=(const priomutex &) = delete; + + void lock() + { + auto ret = pthread_mutex_lock(&pt_mutex); + if (ret != 0) + { + // (yangosoft) TODO handle error + initialized = false; + } + } + + void unlock() noexcept + { + pthread_mutex_unlock(&pt_mutex); + } + + bool try_lock() noexcept + { + return pthread_mutex_trylock(&pt_mutex) == 0; + } + + native_handle_type native_handle() noexcept + { + return &pt_mutex; + } + + bool is_lock_free() const noexcept + { + return false; + } + + bool is_initialized() const noexcept + { + return initialized; + } + + private: + pthread_mutex_t pt_mutex; + bool initialized; + }; +} // namespace CppUtils2 \ No newline at end of file diff --git a/src/test/cpputils2_tests.cpp b/src/test/cpputils2_tests.cpp index 6a906f2..3840dac 100644 --- a/src/test/cpputils2_tests.cpp +++ b/src/test/cpputils2_tests.cpp @@ -14,6 +14,7 @@ #include "cpputils2/linux/net/socket/udpsocketserver.hpp" #include "cpputils2/linux/net/socket/udsclient.hpp" #include "cpputils2/linux/net/socket/udsserver.hpp" +#include "cpputils2/linux/priomutex/priomutex.hpp" #include "cpputils2/linux/sched/sched.hpp" #include "cpputils2/linux/shm/shm.hpp" #include "cpputils2/linux/thread/thread.hpp" @@ -168,6 +169,15 @@ namespace EXPECT_EQ(CppUtils2::Result::RET_OK, CppUtils2::Result::RET_OK); } + TEST(PriorityMutex, PriorityMutex) + { + CppUtils2::priomutex mutex; + bool is_initialized = mutex.is_initialized(); + EXPECT_TRUE(is_initialized); + mutex.lock(); + mutex.unlock(); + } + #endif #ifdef _WIN32 From d6ab9d64ff2ea01f60030fe38eb0546b35b1459b Mon Sep 17 00:00:00 2001 From: yangosoft Date: Mon, 10 Feb 2025 17:49:42 +0100 Subject: [PATCH 5/5] Renamed priomutex to PrioMutex --- src/include/cpputils2/linux/priomutex/priomutex.hpp | 10 +++++----- src/test/cpputils2_tests.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/include/cpputils2/linux/priomutex/priomutex.hpp b/src/include/cpputils2/linux/priomutex/priomutex.hpp index 54cc1ff..4ef4f67 100644 --- a/src/include/cpputils2/linux/priomutex/priomutex.hpp +++ b/src/include/cpputils2/linux/priomutex/priomutex.hpp @@ -7,13 +7,13 @@ namespace CppUtils2 { - class priomutex + class PrioMutex { public: using native_handle_type = pthread_mutex_t *; - priomutex() + PrioMutex() { initialized = false; pthread_mutexattr_t attr; @@ -39,13 +39,13 @@ namespace CppUtils2 initialized = true; } - virtual ~priomutex() + virtual ~PrioMutex() { pthread_mutex_destroy(&pt_mutex); } - priomutex(const priomutex &) = delete; - priomutex &operator=(const priomutex &) = delete; + PrioMutex(const PrioMutex &) = delete; + PrioMutex &operator=(const PrioMutex &) = delete; void lock() { diff --git a/src/test/cpputils2_tests.cpp b/src/test/cpputils2_tests.cpp index 3840dac..c32eb0d 100644 --- a/src/test/cpputils2_tests.cpp +++ b/src/test/cpputils2_tests.cpp @@ -171,7 +171,7 @@ namespace TEST(PriorityMutex, PriorityMutex) { - CppUtils2::priomutex mutex; + CppUtils2::PrioMutex mutex; bool is_initialized = mutex.is_initialized(); EXPECT_TRUE(is_initialized); mutex.lock();