Kurlyk
Loading...
Searching...
No Matches
NetworkWorker.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef KURLYK_HEADER_KURLYK_CORE_NETWORK_WORKER_HPP_INCLUDED
3#define KURLYK_HEADER_KURLYK_CORE_NETWORK_WORKER_HPP_INCLUDED
4
7
8#define KURLYK_HANDLE_ERROR(e, msg) \
9 ::kurlyk::core::NetworkWorker::get_instance().handle_error((e), (msg), __FILE__, __LINE__, __FUNCTION__)
10
11#include <thread>
12
13namespace kurlyk {
14namespace core {
15
22 public:
23 using ErrorHandler = std::function<void(const std::exception&, const char*, const char*, int, const char*)>;
24
28 static NetworkWorker* instance = new NetworkWorker();
29 return *instance;
30 }
31
35 std::lock_guard<std::mutex> lock(m_error_handlers_mutex);
36 m_error_handlers.push_back(std::move(handler));
37 }
38
46 const std::exception& e,
47 const char* msg,
48 const char* file,
49 int line,
50 const char* func) {
51
52 std::unique_lock<std::mutex> lock(m_error_handlers_mutex);
53 if (m_error_handlers.empty()) return;
54 std::vector<ErrorHandler> handlers = m_error_handlers;
55 lock.unlock();
56
57 for (const auto& handler : handlers) {
58 try {
59 handler(e, msg, file, line, func);
60 } catch (...) {
61 // Never let handler crash the system
62 }
63 }
64 }
65
73 std::exception_ptr eptr,
74 const char* msg,
75 const char* file,
76 int line,
77 const char* func) {
78
79 if (!eptr) return;
80
81 try {
82 std::rethrow_exception(eptr);
83 } catch (const std::exception& e) {
84 handle_error(e, msg, file, line, func);
85 } catch (...) {
86 const std::runtime_error unknown("Unknown non-std::exception");
87 handle_error(unknown, msg, file, line, func);
88 }
89 }
90
92 bool is_worker_thread() const {
93 return std::this_thread::get_id() == m_worker_thread_id;
94 }
95
98 void add_task(std::function<void()> task) {
99 std::unique_lock<std::mutex> lock(m_tasks_list_mutex);
100 m_tasks_list.push_back(std::move(task));
101 lock.unlock();
102 notify();
103 }
104
108 std::lock_guard<std::mutex> lock(m_managers_mutex);
109 if (std::find(m_managers.begin(), m_managers.end(), manager) == m_managers.end()) {
110 m_managers.push_back(manager);
111 }
112 }
113
117 void process() {
118 std::unique_lock<std::mutex> lock(m_managers_mutex);
119 for (auto* m : m_managers) m->process();
120 lock.unlock();
122 }
123
127 void notify() {
128 std::lock_guard<std::mutex> locker(m_notify_mutex);
129 m_notify = true;
130 m_notify_condition.notify_one();
131 }
132
138 void start(const bool use_async) {
139 std::unique_lock<std::mutex> locker(m_is_worker_started_mutex);
140 if (m_is_worker_started) return;
141 m_is_worker_started = true;
142 if (!use_async) return;
143 locker.unlock();
144
145 m_future = std::async(
146 std::launch::async,
147 [this] {
148 m_worker_thread_id = std::this_thread::get_id();
149 for (;;) {
150 std::unique_lock<std::mutex> locker(m_notify_mutex);
151 m_notify_condition.wait(locker, [this]() { return m_notify; });
152 m_notify = false;
153 locker.unlock();
154
155 if (m_shutdown) {
156 shutdown();
157 return;
158 }
159
160 while (is_loaded()) {
161 process();
162 if (m_shutdown) {
163 shutdown();
164 return;
165 }
166
167 std::unique_lock<std::mutex> locker(m_notify_mutex);
168 m_notify_condition.wait_for(locker, std::chrono::milliseconds(1), [this] {
169 return m_notify || m_shutdown;
170 });
171 m_notify = false;
172 locker.unlock();
173
174 if (m_shutdown) {
175 shutdown();
176 return;
177 }
178 }
179
180 if (m_shutdown) {
181 shutdown();
182 return;
183 }
184 }
185 }).share();
186 }
187
191 void stop() {
192 {
193 std::lock_guard<std::mutex> locker(m_is_worker_started_mutex);
194 if (!m_is_worker_started && !m_future.valid()) return;
195 }
196
197 const bool was_shutdown = m_shutdown.exchange(true);
198 if (!m_future.valid()) {
199 if (!was_shutdown) shutdown();
200 return;
201 }
202
203 notify();
204 try {
205 m_future.wait();
206 m_future.get();
207 } catch(...) {
208 KURLYK_HANDLE_ERROR(std::current_exception(), "Exception during NetworkWorker shutdown");
209 };
210 }
211
215 void shutdown() {
216 std::unique_lock<std::mutex> lock(m_managers_mutex);
217 for (auto* m : m_managers) m->shutdown();
218 lock.unlock();
220 }
221
222 private:
223 std::shared_future<void> m_future;
224 std::atomic<bool> m_shutdown = ATOMIC_VAR_INIT(false);
225 std::thread::id m_worker_thread_id;
226 std::mutex m_notify_mutex;
227 std::condition_variable m_notify_condition;
228 bool m_notify = false;
230 bool m_is_worker_started = false;
231 mutable std::mutex m_tasks_list_mutex;
232 std::list<std::function<void()>> m_tasks_list;
233 mutable std::mutex m_managers_mutex;
234 std::vector<INetworkTaskManager*> m_managers;
236 std::vector<ErrorHandler> m_error_handlers;
237
238
244
247 stop();
248 }
249
251 NetworkWorker(const NetworkWorker&) = delete;
252
255
260 std::unique_lock<std::mutex> lock(m_tasks_list_mutex);
261 if (m_tasks_list.empty()) return;
262 auto tasks_list = std::move(m_tasks_list);
263 m_tasks_list.clear();
264 lock.unlock();
265 for (auto &item : tasks_list) {
266 item();
267 }
268 tasks_list.clear();
269 }
270
273 bool has_pending_tasks() const {
274 std::lock_guard<std::mutex> lock(m_tasks_list_mutex);
275 return !m_tasks_list.empty();
276 }
277
282 bool is_loaded() const {
283 std::unique_lock<std::mutex> lock(m_managers_mutex);
284 for (auto* m : m_managers) {
285 if (m->is_loaded()) return true;
286 }
287 lock.unlock();
288 return has_pending_tasks();
289 }
290
291 }; // NetworkWorker
292
293} // namespace core
294} // namespace kurlyk
295
296#endif // KURLYK_HEADER_KURLYK_CORE_NETWORK_WORKER_HPP_INCLUDED
#define KURLYK_HANDLE_ERROR(e, msg)
Interface for modules managed by NetworkWorker (e.g., HTTP, WebSocket).
void stop()
Stops the worker thread, ensuring all tasks are completed.
void notify()
Notifies the worker to begin processing requests or tasks.
bool m_is_worker_started
Flag indicating if the worker thread is started.
bool has_pending_tasks() const
Checks if there are any pending tasks in the task list.
static NetworkWorker & get_instance()
Get the singleton instance of NetworkWorker.
void add_task(std::function< void()> task)
Adds a task to the queue and notifies the worker thread.
bool is_loaded() const
Checks if the NetworkWorker has pending tasks or active network events.
void handle_error(const std::exception &e, const char *msg, const char *file, int line, const char *func)
Dispatches an exception to all registered error handlers.
std::atomic< bool > m_shutdown
Flag indicating if shutdown has been requested.
NetworkWorker(const NetworkWorker &)=delete
Deleted copy constructor to enforce the singleton pattern.
void handle_error(std::exception_ptr eptr, const char *msg, const char *file, int line, const char *func)
Handles an exception captured as exception_ptr.
void process()
Processes all queued tasks and active HTTP and WebSocket requests.
std::condition_variable m_notify_condition
Condition variable for notifying the worker.
NetworkWorker & operator=(const NetworkWorker &)=delete
Deleted copy assignment operator to enforce the singleton pattern.
std::shared_future< void > m_future
Future for managing asynchronous worker execution.
std::vector< INetworkTaskManager * > m_managers
List of registered network task managers.
void process_tasks()
Processes all tasks in the task list, then clears the list.
void start(const bool use_async)
Starts the worker thread for asynchronous task processing.
bool m_notify
Flag indicating whether a notification is pending. Access is always guarded by m_notify_mutex; atomic...
std::list< std::function< void()> > m_tasks_list
List of tasks queued for processing by the worker.
void register_manager(INetworkTaskManager *manager)
Registers a network task manager to be managed by the NetworkWorker.
void add_error_handler(ErrorHandler handler)
Registers a callback for handling network errors.
std::mutex m_managers_mutex
Mutex protecting access to registered managers.
~NetworkWorker()
Private destructor, ensuring the worker thread stops on destruction.
void shutdown()
Shuts down the worker, clearing all active requests and pending tasks.
std::mutex m_tasks_list_mutex
Mutex for protecting access to the task list.
std::mutex m_is_worker_started_mutex
Mutex to control worker thread initialization.
NetworkWorker()
Private constructor to enforce singleton pattern.
bool is_worker_thread() const
Returns true if the calling thread is the worker thread.
std::thread::id m_worker_thread_id
ID of the async worker thread, if started.
std::mutex m_error_handlers_mutex
Mutex guarding the error handler list.
std::function< void(const std::exception &, const char *, const char *, int, const char *)> ErrorHandler
std::mutex m_notify_mutex
Mutex for managing worker notifications.
std::vector< ErrorHandler > m_error_handlers
Collection of registered error handlers.
bool is_valid_email_id(const std::string &str)
Validates an email address format.
std::string convert_user_agent_to_sec_ch_ua(const std::string &user_agent)
Converts a User-Agent string to a sec-ch-ua header value.
Primary namespace for the Kurlyk library, encompassing initialization, request management,...