ThreadSchedule 1.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
pthread_wrapper.hpp
1#pragma once
2
3#include "expected.hpp"
4#include "scheduler_policy.hpp"
5#include <atomic>
6#include <exception>
7#include <functional>
8#include <memory>
9#include <optional>
10#include <string>
11
12#ifdef _WIN32
13#include <windows.h>
14// PThreadWrapper is not available on Windows as it's POSIX-specific
15// Users should use ThreadWrapper or JThreadWrapper instead
16#else
17#include <pthread.h>
18#endif
19
20namespace threadschedule
21{
22
23#ifndef _WIN32
27class PThreadWrapper
28{
29 public:
30 using native_handle_type = pthread_t;
31 using id = pthread_t;
32
33 PThreadWrapper() : thread_(0), joined_(false)
34 {
35 }
36
37 template <typename F, typename... Args>
38 explicit PThreadWrapper(F&& func, Args&&... args) : thread_(0), joined_(false)
39 {
40
41 // Store the callable in a way pthread can handle
42 auto callable =
43 std::make_unique<std::function<void()>>(std::bind(std::forward<F>(func), std::forward<Args>(args)...));
44
45 int const result = pthread_create(&thread_, nullptr, thread_function, callable.release());
46
47 if (result != 0)
48 {
49 throw std::runtime_error("Failed to create pthread: " + std::to_string(result));
50 }
51 }
52
53 // Non-copyable
54 PThreadWrapper(PThreadWrapper const&) = delete;
55 auto operator=(PThreadWrapper const&) -> PThreadWrapper& = delete;
56
57 // Movable
58 PThreadWrapper(PThreadWrapper&& other) noexcept : thread_(other.thread_), joined_(other.joined_.load())
59 {
60 other.thread_ = 0;
61 other.joined_.store(true);
62 }
63
64 auto operator=(PThreadWrapper&& other) noexcept -> PThreadWrapper&
65 {
66 if (this != &other)
67 {
68 if (joinable())
69 {
70 join();
71 }
72 thread_ = other.thread_;
73 joined_.store(other.joined_.load());
74 other.thread_ = 0;
75 other.joined_.store(true);
76 }
77 return *this;
78 }
79
80 ~PThreadWrapper()
81 {
82 if (joinable())
83 {
84 join();
85 }
86 }
87
88 // Thread management
89 void join()
90 {
91 if (joinable())
92 {
93 void* retval;
94 int const result = pthread_join(thread_, &retval);
95 if (result == 0)
96 {
97 joined_ = true;
98 }
99 }
100 }
101
102 void detach()
103 {
104 if (joinable())
105 {
106 int const result = pthread_detach(thread_);
107 if (result == 0)
108 {
109 joined_ = true;
110 }
111 }
112 }
113
114 [[nodiscard]] auto joinable() const -> bool
115 {
116 return thread_ != 0 && !joined_;
117 }
118
119 [[nodiscard]] auto get_id() const -> id
120 {
121 return thread_;
122 }
123 [[nodiscard]] auto native_handle() const -> native_handle_type
124 {
125 return thread_;
126 }
127
128 // Extended pthread functionality
129 [[nodiscard]] auto set_name(std::string const& name) const -> expected<void, std::error_code>
130 {
131 if (name.length() > 15)
132 return expected<void, std::error_code>(unexpect, std::make_error_code(std::errc::invalid_argument));
133 if (pthread_setname_np(thread_, name.c_str()) == 0)
134 return {};
135 return expected<void, std::error_code>(unexpect, std::error_code(errno, std::generic_category()));
136 }
137
138 [[nodiscard]] auto get_name() const -> std::optional<std::string>
139 {
140 char name[16]; // Linux limit + 1
141 if (pthread_getname_np(thread_, name, sizeof(name)) == 0)
142 {
143 return std::string(name);
144 }
145 return std::nullopt;
146 }
147
148 [[nodiscard]] auto set_priority(ThreadPriority priority) const -> expected<void, std::error_code>
149 {
150 int const policy = SCHED_OTHER;
151 auto params_result = SchedulerParams::create_for_policy(SchedulingPolicy::OTHER, priority);
152
153 if (!params_result.has_value())
154 {
155 return unexpected(params_result.error());
156 }
157
158 if (pthread_setschedparam(thread_, policy, &params_result.value()) == 0)
159 return {};
160 return unexpected(std::error_code(errno, std::generic_category()));
161 }
162
163 [[nodiscard]] auto set_scheduling_policy(SchedulingPolicy policy, ThreadPriority priority) const
165 {
166 int const policy_int = static_cast<int>(policy);
167 auto params_result = SchedulerParams::create_for_policy(policy, priority);
168
169 if (!params_result.has_value())
170 {
171 return unexpected(params_result.error());
172 }
173
174 if (pthread_setschedparam(thread_, policy_int, &params_result.value()) == 0)
175 return {};
176 return unexpected(std::error_code(errno, std::generic_category()));
177 }
178
179 [[nodiscard]] auto set_affinity(ThreadAffinity const& affinity) const -> expected<void, std::error_code>
180 {
181 if (pthread_setaffinity_np(thread_, sizeof(cpu_set_t), &affinity.native_handle()) == 0)
182 return {};
183 return unexpected(std::error_code(errno, std::generic_category()));
184 }
185
186 [[nodiscard]] auto get_affinity() const -> std::optional<ThreadAffinity>
187 {
188 ThreadAffinity affinity;
189 if (pthread_getaffinity_np(thread_, sizeof(cpu_set_t), const_cast<cpu_set_t*>(&affinity.native_handle())) == 0)
190 {
191 return affinity;
192 }
193 return std::nullopt;
194 }
195
196 // Cancellation support
197 [[nodiscard]] auto cancel() const -> expected<void, std::error_code>
198 {
199 if (pthread_cancel(thread_) == 0)
200 return {};
201 return unexpected(std::error_code(errno, std::generic_category()));
202 }
203
204 static auto set_cancel_state(bool enabled) -> expected<void, std::error_code>
205 {
206 int const state = enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE;
207 int old_state;
208 if (pthread_setcancelstate(state, &old_state) == 0)
209 return {};
210 return unexpected(std::error_code(errno, std::generic_category()));
211 }
212
213 static auto set_cancel_type(bool asynchronous) -> expected<void, std::error_code>
214 {
215 int const type = asynchronous ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED;
216 int old_type;
217 if (pthread_setcanceltype(type, &old_type) == 0)
218 return {};
219 return unexpected(std::error_code(errno, std::generic_category()));
220 }
221
222 // Factory methods
223 template <typename F, typename... Args>
224 static auto create_with_config(std::string const& name, SchedulingPolicy policy, ThreadPriority priority, F&& f,
225 Args&&... args) -> PThreadWrapper
226 {
227
228 PThreadWrapper wrapper(std::forward<F>(f), std::forward<Args>(args)...);
229 (void)wrapper.set_name(name);
230 (void)wrapper.set_scheduling_policy(policy, priority);
231 return wrapper;
232 }
233
234 template <typename F, typename... Args>
235 static auto create_with_attributes(pthread_attr_t const& attr, F&& func, Args&&... args) -> PThreadWrapper
236 {
237
238 PThreadWrapper wrapper;
239 auto callable =
240 std::make_unique<std::function<void()>>(std::bind(std::forward<F>(func), std::forward<Args>(args)...));
241
242 int const result = pthread_create(&wrapper.thread_, &attr, thread_function, callable.release());
243
244 if (result != 0)
245 {
246 throw std::runtime_error("Failed to create pthread with attributes: " + std::to_string(result));
247 }
248
249 return wrapper;
250 }
251
252 private:
253 pthread_t thread_;
254 std::atomic<bool> joined_;
255
256 static auto thread_function(void* arg) -> void*
257 {
258 std::unique_ptr<std::function<void()>> func(static_cast<std::function<void()>*>(arg));
259
260 try
261 {
262 (*func)();
263 }
264 catch (...)
265 {
266 // Handle exceptions - could add logging here
267 }
268
269 return nullptr;
270 }
271};
272
276class PThreadAttributes
277{
278 public:
279 PThreadAttributes()
280 {
281 if (pthread_attr_init(&attr_) != 0)
282 {
283 throw std::runtime_error("Failed to initialize pthread attributes");
284 }
285 }
286
287 ~PThreadAttributes()
288 {
289 pthread_attr_destroy(&attr_);
290 }
291
292 // Non-copyable
293 PThreadAttributes(PThreadAttributes const&) = delete;
294 auto operator=(PThreadAttributes const&) -> PThreadAttributes& = delete;
295
296 // Movable
297 PThreadAttributes(PThreadAttributes&& other) noexcept : attr_(other.attr_)
298 {
299 if (pthread_attr_init(&other.attr_) != 0)
300 {
301 std::terminate(); // Can't throw from move constructor
302 }
303 }
304
305 auto operator=(PThreadAttributes&& other) noexcept -> PThreadAttributes&
306 {
307 if (this != &other)
308 {
309 pthread_attr_destroy(&attr_);
310 attr_ = other.attr_;
311 if (pthread_attr_init(&other.attr_) != 0)
312 {
313 std::terminate(); // Can't throw from move assignment
314 }
315 }
316 return *this;
317 }
318
319 [[nodiscard]] auto get() const -> pthread_attr_t const&
320 {
321 return attr_;
322 }
323 auto get() -> pthread_attr_t&
324 {
325 return attr_;
326 }
327
328 // Attribute setters
329 auto set_detach_state(bool detached) -> bool
330 {
331 int const state = detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE;
332 return pthread_attr_setdetachstate(&attr_, state) == 0;
333 }
334
335 auto set_stack_size(size_t stack_size) -> bool
336 {
337 return pthread_attr_setstacksize(&attr_, stack_size) == 0;
338 }
339
340 auto set_guard_size(size_t guard_size) -> bool
341 {
342 return pthread_attr_setguardsize(&attr_, guard_size) == 0;
343 }
344
345 auto set_scheduling_policy(SchedulingPolicy policy) -> bool
346 {
347 int const policy_int = static_cast<int>(policy);
348 return pthread_attr_setschedpolicy(&attr_, policy_int) == 0;
349 }
350
351 auto set_scheduling_parameter(ThreadPriority priority) -> bool
352 {
353 sched_param param{};
354 param.sched_priority = priority.value();
355 return pthread_attr_setschedparam(&attr_, &param) == 0;
356 }
357
358 auto set_inherit_sched(bool inherit) -> bool
359 {
360 int const inherit_val = inherit ? PTHREAD_INHERIT_SCHED : PTHREAD_EXPLICIT_SCHED;
361 return pthread_attr_setinheritsched(&attr_, inherit_val) == 0;
362 }
363
364 auto set_scope(bool system_scope) -> bool
365 {
366 int const scope = system_scope ? PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS;
367 return pthread_attr_setscope(&attr_, scope) == 0;
368 }
369
370 // Attribute getters
371 [[nodiscard]] auto get_detach_state() const -> std::optional<bool>
372 {
373 int state;
374 if (pthread_attr_getdetachstate(&attr_, &state) == 0)
375 {
376 return state == PTHREAD_CREATE_DETACHED;
377 }
378 return std::nullopt;
379 }
380
381 [[nodiscard]] auto get_stack_size() const -> std::optional<size_t>
382 {
383 size_t stack_size;
384 if (pthread_attr_getstacksize(&attr_, &stack_size) == 0)
385 {
386 return stack_size;
387 }
388 return std::nullopt;
389 }
390
391 [[nodiscard]] auto get_guard_size() const -> std::optional<size_t>
392 {
393 size_t guard_size;
394 if (pthread_attr_getguardsize(&attr_, &guard_size) == 0)
395 {
396 return guard_size;
397 }
398 return std::nullopt;
399 }
400
401 private:
402 pthread_attr_t attr_;
403};
404
408class PThreadMutex
409{
410 public:
411 PThreadMutex()
412 {
413 if (pthread_mutex_init(&mutex_, nullptr) != 0)
414 {
415 throw std::runtime_error("Failed to initialize pthread mutex");
416 }
417 }
418
419 explicit PThreadMutex(pthread_mutexattr_t const* attr)
420 {
421 if (pthread_mutex_init(&mutex_, attr) != 0)
422 {
423 throw std::runtime_error("Failed to initialize pthread mutex with attributes");
424 }
425 }
426
427 ~PThreadMutex()
428 {
429 pthread_mutex_destroy(&mutex_);
430 }
431
432 PThreadMutex(PThreadMutex const&) = delete;
433 auto operator=(PThreadMutex const&) -> PThreadMutex& = delete;
434
435 void lock()
436 {
437 if (pthread_mutex_lock(&mutex_) != 0)
438 {
439 throw std::runtime_error("Failed to lock pthread mutex");
440 }
441 }
442
443 auto try_lock() -> bool
444 {
445 return pthread_mutex_trylock(&mutex_) == 0;
446 }
447
448 void unlock()
449 {
450 if (pthread_mutex_unlock(&mutex_) != 0)
451 {
452 throw std::runtime_error("Failed to unlock pthread mutex");
453 }
454 }
455
456 auto native_handle() -> pthread_mutex_t*
457 {
458 return &mutex_;
459 }
460
461 private:
462 pthread_mutex_t mutex_;
463};
464
465#endif // !_WIN32
466
467} // namespace threadschedule
Thread priority wrapper with validation.