Skip to content

Commit 17c179a

Browse files
authored
Merge pull request #7 from yangosoft/feature/thread-utils
Feature/thread utils
2 parents ee7d7a1 + d6ab9d6 commit 17c179a

8 files changed

Lines changed: 325 additions & 0 deletions

File tree

examples/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ FetchContent_Declare(
1010
FetchContent_MakeAvailable(spdlog)
1111

1212
add_subdirectory(shared_memory)
13+
14+
if (WIN32)
15+
16+
else()
17+
add_subdirectory(linux_deadline)
18+
endif()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
cmake_minimum_required(VERSION 3.14)
2+
project(example_linux_deadline VERSION 1.0.0 LANGUAGES CXX)
3+
4+
5+
if(NOT TARGET CPPUTILS2::cpputils2)
6+
message("Not target!")
7+
find_package(CPPUTILS2 REQUIRED)
8+
9+
Include(FetchContent)
10+
FetchContent_Declare(
11+
spdlog
12+
GIT_REPOSITORY https://github.com/gabime/spdlog.git
13+
GIT_TAG v1.x
14+
)
15+
16+
FetchContent_MakeAvailable(spdlog)
17+
endif()
18+
19+
20+
add_executable(linux_deadline src/main.cpp)
21+
22+
target_link_libraries(linux_deadline PRIVATE CPPUTILS2::cpputils2 spdlog::spdlog)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
#include <cpputils2/cpputils2.hpp>
3+
#include <cpputils2/linux/sched/sched.hpp>
4+
5+
#include <atomic>
6+
#include <chrono>
7+
#include <csignal>
8+
#include <iostream>
9+
#include <spdlog/spdlog.h>
10+
11+
#include <linux/sched.h>
12+
13+
const int32_t expected = 42;
14+
const std::string file_name("test_shm");
15+
16+
std::atomic_flag run_thread = ATOMIC_FLAG_INIT;
17+
18+
void signalHandler(int signum)
19+
{
20+
if (signum == SIGXCPU)
21+
{
22+
SPDLOG_INFO("Caught signal SIGXCPU. We are overruning the deadline");
23+
run_thread.clear();
24+
}
25+
else if (signum == SIGINT)
26+
{
27+
run_thread.clear();
28+
}
29+
}
30+
31+
int main(int argc, char **argv)
32+
{
33+
SPDLOG_INFO("cpputils2 version {}", CppUtils2::VERSION);
34+
SPDLOG_INFO("Test deadline Linux scheduler. Ctrl-C to finish test.");
35+
36+
CppUtils2::sched_attr attr;
37+
attr.size = sizeof(CppUtils2::sched_attr);
38+
attr.sched_policy = SCHED_DEADLINE;
39+
attr.sched_priority = 0;
40+
41+
attr.sched_runtime = 200'000'000;
42+
attr.sched_deadline = 500'000'000;
43+
attr.sched_period = 500'000'000; // 1s
44+
attr.sched_flags = SCHED_FLAG_RESET_ON_FORK | SCHED_FLAG_RECLAIM | SCHED_FLAG_DL_OVERRUN;
45+
signal(SIGXCPU, signalHandler);
46+
signal(SIGINT, signalHandler);
47+
48+
run_thread.test_and_set();
49+
50+
auto ret = CppUtils2::set_self_attributes(&attr);
51+
52+
if (!ret.has_value())
53+
{
54+
auto error = ret.error();
55+
SPDLOG_ERROR("Error setting attributes. Error code: {}, errno: {}", error, errno);
56+
return -1;
57+
}
58+
59+
auto start = std::chrono::high_resolution_clock::now();
60+
61+
bool first = true;
62+
uint64_t mean_time_ns = 0;
63+
uint64_t count = 0;
64+
65+
while (run_thread.test())
66+
{
67+
if (first)
68+
{
69+
first = false;
70+
}
71+
else
72+
{
73+
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start).count();
74+
std::cout << "Elapsed: " << elapsed << "ns\n";
75+
start = std::chrono::high_resolution_clock::now();
76+
mean_time_ns += elapsed;
77+
count++;
78+
}
79+
sched_yield();
80+
}
81+
82+
SPDLOG_INFO("Exiting. Mean activation time {}ns", mean_time_ns / count);
83+
return 0;
84+
}

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ else()
4848
include/cpputils2/linux/futex/futex.hpp
4949
include/cpputils2/linux/futex/shared_futex.hpp
5050
include/cpputils2/linux/thread/thread.hpp
51+
include/cpputils2/linux/sched/sched.hpp
52+
include/cpputils2/linux/priomutex/priomutex.hpp
5153
)
5254

5355
endif()
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#pragma once
2+
3+
#include "cpputils2/common/types.hpp"
4+
5+
#include <pthread.h>
6+
7+
namespace CppUtils2
8+
{
9+
10+
class PrioMutex
11+
{
12+
13+
public:
14+
using native_handle_type = pthread_mutex_t *;
15+
16+
PrioMutex()
17+
{
18+
initialized = false;
19+
pthread_mutexattr_t attr;
20+
21+
int ret = pthread_mutexattr_init(&attr);
22+
if (ret != 0)
23+
{
24+
return;
25+
}
26+
27+
ret = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
28+
if (ret != 0)
29+
{
30+
return;
31+
}
32+
33+
ret = pthread_mutex_init(&pt_mutex, &attr);
34+
if (ret != 0)
35+
{
36+
return;
37+
}
38+
39+
initialized = true;
40+
}
41+
42+
virtual ~PrioMutex()
43+
{
44+
pthread_mutex_destroy(&pt_mutex);
45+
}
46+
47+
PrioMutex(const PrioMutex &) = delete;
48+
PrioMutex &operator=(const PrioMutex &) = delete;
49+
50+
void lock()
51+
{
52+
auto ret = pthread_mutex_lock(&pt_mutex);
53+
if (ret != 0)
54+
{
55+
// (yangosoft) TODO handle error
56+
initialized = false;
57+
}
58+
}
59+
60+
void unlock() noexcept
61+
{
62+
pthread_mutex_unlock(&pt_mutex);
63+
}
64+
65+
bool try_lock() noexcept
66+
{
67+
return pthread_mutex_trylock(&pt_mutex) == 0;
68+
}
69+
70+
native_handle_type native_handle() noexcept
71+
{
72+
return &pt_mutex;
73+
}
74+
75+
bool is_lock_free() const noexcept
76+
{
77+
return false;
78+
}
79+
80+
bool is_initialized() const noexcept
81+
{
82+
return initialized;
83+
}
84+
85+
private:
86+
pthread_mutex_t pt_mutex;
87+
bool initialized;
88+
};
89+
} // namespace CppUtils2
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#pragma once
2+
3+
#include "cpputils2/common/types.hpp"
4+
5+
#include <cassert>
6+
#include <cstdint>
7+
#include <expected>
8+
#include <pthread.h>
9+
#include <sched.h>
10+
#include <sys/syscall.h>
11+
#include <unistd.h>
12+
13+
#include <span>
14+
15+
namespace CppUtils2
16+
{
17+
18+
// (yangosoft) As man 2 page says, this is a generic structure for scheduling attributes
19+
struct sched_attr
20+
{
21+
uint32_t size; /* Size of this structure */
22+
uint32_t sched_policy = 0; /* Policy (SCHED_*) */
23+
uint64_t sched_flags = 0; /* Flags */
24+
int32_t sched_nice = 0; /* Nice value (SCHED_OTHER,
25+
SCHED_BATCH) */
26+
uint32_t sched_priority = 0; /* Static priority (SCHED_FIFO,
27+
SCHED_RR) */
28+
/* Remaining fields are for SCHED_DEADLINE */
29+
uint64_t sched_runtime = 0;
30+
uint64_t sched_deadline = 0;
31+
uint64_t sched_period = 0;
32+
33+
sched_attr() : size(sizeof(sched_attr)) {}
34+
};
35+
36+
int32_t sched_getattr(pid_t pid, sched_attr *attr, unsigned int size, unsigned int flags = 0)
37+
{
38+
return syscall(SYS_sched_getattr, pid, attr, size, flags);
39+
}
40+
41+
int32_t sched_setattr(pid_t pid, const sched_attr *attr, uint32_t flags = 0)
42+
{
43+
return syscall(SYS_sched_setattr, pid, attr, flags);
44+
}
45+
46+
std::expected<Result, int32_t> set_self_attributes(const sched_attr *attr)
47+
{
48+
pid_t me = getpid();
49+
int flags = 0;
50+
51+
int32_t ret = sched_setattr(me, attr, flags);
52+
53+
if (ret != 0)
54+
{
55+
return std::unexpected(ret);
56+
}
57+
58+
return Result::RET_OK;
59+
}
60+
61+
std::expected<Result, int32_t> set_process_core_affinity(pid_t pid, const cpu_set_t *mask)
62+
{
63+
int ret = sched_setaffinity(pid, sizeof(cpu_set_t), mask);
64+
if (ret != 0)
65+
{
66+
return std::unexpected(errno);
67+
}
68+
return Result::RET_OK;
69+
}
70+
71+
std::expected<Result, int32_t> set_self_core_affinity(const cpu_set_t *mask)
72+
{
73+
return set_process_core_affinity(getpid(), mask);
74+
}
75+
76+
std::expected<Result, int32_t> set_process_core_affinity(pid_t pid, const std::span<uint32_t, std::dynamic_extent> &mask)
77+
{
78+
79+
cpu_set_t cpuset;
80+
CPU_ZERO(&cpuset);
81+
for (auto core : mask)
82+
{
83+
CPU_SET(core, &cpuset);
84+
}
85+
86+
return set_process_core_affinity(pid, &cpuset);
87+
}
88+
89+
std::expected<Result, int32_t> set_self_core_affinity(const std::span<uint32_t, std::dynamic_extent> &mask)
90+
{
91+
return set_process_core_affinity(getpid(), mask);
92+
}
93+
94+
} // namespace CppUtils2

src/include/cpputils2/linux/thread/thread.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,16 @@ namespace CppUtils2
9494
return config;
9595
}
9696

97+
int32_t pin_thread_to_core(std::thread &thread, const int core_id)
98+
{
99+
cpu_set_t cpuset;
100+
CPU_ZERO(&cpuset);
101+
CPU_SET(core_id, &cpuset);
102+
103+
// ensure that native_handler() is a pthread_t
104+
assert(typeid(thread.native_handle()) == typeid(pthread_t));
105+
106+
return pthread_setaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset);
107+
}
108+
97109
}

src/test/cpputils2_tests.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "cpputils2/linux/net/socket/udpsocketserver.hpp"
1515
#include "cpputils2/linux/net/socket/udsclient.hpp"
1616
#include "cpputils2/linux/net/socket/udsserver.hpp"
17+
#include "cpputils2/linux/priomutex/priomutex.hpp"
18+
#include "cpputils2/linux/sched/sched.hpp"
1719
#include "cpputils2/linux/shm/shm.hpp"
1820
#include "cpputils2/linux/thread/thread.hpp"
1921
#endif
@@ -162,6 +164,20 @@ namespace
162164
t.join();
163165
}
164166

167+
TEST(Scheduler, Scheduler)
168+
{
169+
EXPECT_EQ(CppUtils2::Result::RET_OK, CppUtils2::Result::RET_OK);
170+
}
171+
172+
TEST(PriorityMutex, PriorityMutex)
173+
{
174+
CppUtils2::PrioMutex mutex;
175+
bool is_initialized = mutex.is_initialized();
176+
EXPECT_TRUE(is_initialized);
177+
mutex.lock();
178+
mutex.unlock();
179+
}
180+
165181
#endif
166182

167183
#ifdef _WIN32

0 commit comments

Comments
 (0)