librats 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +1 -0
  3. package/lib/index.d.ts +2 -1
  4. package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
  5. package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
  6. package/native-src/CMakeLists.txt +360 -0
  7. package/native-src/LICENSE +21 -0
  8. package/native-src/src/bencode.cpp +485 -0
  9. package/native-src/src/bencode.h +145 -0
  10. package/native-src/src/bittorrent.cpp +3682 -0
  11. package/native-src/src/bittorrent.h +731 -0
  12. package/native-src/src/dht.cpp +2460 -0
  13. package/native-src/src/dht.h +508 -0
  14. package/native-src/src/encrypted_socket.cpp +817 -0
  15. package/native-src/src/encrypted_socket.h +239 -0
  16. package/native-src/src/file_transfer.cpp +1808 -0
  17. package/native-src/src/file_transfer.h +567 -0
  18. package/native-src/src/fs.cpp +639 -0
  19. package/native-src/src/fs.h +108 -0
  20. package/native-src/src/gossipsub.cpp +1137 -0
  21. package/native-src/src/gossipsub.h +403 -0
  22. package/native-src/src/ice.cpp +1386 -0
  23. package/native-src/src/ice.h +328 -0
  24. package/native-src/src/json.hpp +25526 -0
  25. package/native-src/src/krpc.cpp +558 -0
  26. package/native-src/src/krpc.h +145 -0
  27. package/native-src/src/librats.cpp +2735 -0
  28. package/native-src/src/librats.h +1732 -0
  29. package/native-src/src/librats_bittorrent.cpp +167 -0
  30. package/native-src/src/librats_c.cpp +1333 -0
  31. package/native-src/src/librats_c.h +239 -0
  32. package/native-src/src/librats_encryption.cpp +123 -0
  33. package/native-src/src/librats_file_transfer.cpp +226 -0
  34. package/native-src/src/librats_gossipsub.cpp +293 -0
  35. package/native-src/src/librats_ice.cpp +515 -0
  36. package/native-src/src/librats_logging.cpp +158 -0
  37. package/native-src/src/librats_mdns.cpp +171 -0
  38. package/native-src/src/librats_nat.cpp +571 -0
  39. package/native-src/src/librats_persistence.cpp +815 -0
  40. package/native-src/src/logger.h +412 -0
  41. package/native-src/src/mdns.cpp +1178 -0
  42. package/native-src/src/mdns.h +253 -0
  43. package/native-src/src/network_utils.cpp +598 -0
  44. package/native-src/src/network_utils.h +162 -0
  45. package/native-src/src/noise.cpp +981 -0
  46. package/native-src/src/noise.h +227 -0
  47. package/native-src/src/os.cpp +371 -0
  48. package/native-src/src/os.h +40 -0
  49. package/native-src/src/rats_export.h +17 -0
  50. package/native-src/src/sha1.cpp +163 -0
  51. package/native-src/src/sha1.h +42 -0
  52. package/native-src/src/socket.cpp +1376 -0
  53. package/native-src/src/socket.h +309 -0
  54. package/native-src/src/stun.cpp +484 -0
  55. package/native-src/src/stun.h +349 -0
  56. package/native-src/src/threadmanager.cpp +105 -0
  57. package/native-src/src/threadmanager.h +53 -0
  58. package/native-src/src/tracker.cpp +1110 -0
  59. package/native-src/src/tracker.h +268 -0
  60. package/native-src/src/version.cpp +24 -0
  61. package/native-src/src/version.h.in +45 -0
  62. package/native-src/version.rc.in +31 -0
  63. package/package.json +2 -8
  64. package/scripts/build-librats.js +59 -12
  65. package/scripts/prepare-package.js +133 -37
  66. package/src/librats_node.cpp +46 -1
@@ -0,0 +1,349 @@
1
+ #ifndef LIBRATS_STUN_H
2
+ #define LIBRATS_STUN_H
3
+
4
+ #include "socket.h"
5
+ #include <string>
6
+ #include <vector>
7
+ #include <cstdint>
8
+ #include <functional>
9
+ #include <memory>
10
+
11
+ namespace librats {
12
+
13
+ // STUN Protocol Constants
14
+ namespace stun {
15
+ // STUN Message Types (RFC 5389)
16
+ const uint16_t BINDING_REQUEST = 0x0001;
17
+ const uint16_t BINDING_RESPONSE = 0x0101;
18
+ const uint16_t BINDING_ERROR_RESPONSE = 0x0111;
19
+
20
+ // TURN Message Types (RFC 5766)
21
+ const uint16_t ALLOCATE_REQUEST = 0x0003;
22
+ const uint16_t ALLOCATE_RESPONSE = 0x0103;
23
+ const uint16_t ALLOCATE_ERROR_RESPONSE = 0x0113;
24
+ const uint16_t REFRESH_REQUEST = 0x0004;
25
+ const uint16_t REFRESH_RESPONSE = 0x0104;
26
+ const uint16_t SEND_INDICATION = 0x0016;
27
+ const uint16_t DATA_INDICATION = 0x0017;
28
+ const uint16_t CREATE_PERMISSION_REQUEST = 0x0008;
29
+ const uint16_t CREATE_PERMISSION_RESPONSE = 0x0108;
30
+ const uint16_t CHANNEL_BIND_REQUEST = 0x0009;
31
+ const uint16_t CHANNEL_BIND_RESPONSE = 0x0109;
32
+
33
+ // STUN Magic Cookie (RFC 5389)
34
+ const uint32_t MAGIC_COOKIE = 0x2112A442;
35
+
36
+ // STUN Attribute Types (RFC 5389)
37
+ const uint16_t ATTR_MAPPED_ADDRESS = 0x0001;
38
+ const uint16_t ATTR_RESPONSE_ADDRESS = 0x0002;
39
+ const uint16_t ATTR_CHANGE_REQUEST = 0x0003;
40
+ const uint16_t ATTR_SOURCE_ADDRESS = 0x0004;
41
+ const uint16_t ATTR_CHANGED_ADDRESS = 0x0005;
42
+ const uint16_t ATTR_USERNAME = 0x0006;
43
+ const uint16_t ATTR_PASSWORD = 0x0007;
44
+ const uint16_t ATTR_MESSAGE_INTEGRITY = 0x0008;
45
+ const uint16_t ATTR_ERROR_CODE = 0x0009;
46
+ const uint16_t ATTR_UNKNOWN_ATTRIBUTES = 0x000A;
47
+ const uint16_t ATTR_REFLECTED_FROM = 0x000B;
48
+ const uint16_t ATTR_REALM = 0x0014;
49
+ const uint16_t ATTR_NONCE = 0x0015;
50
+ const uint16_t ATTR_XOR_MAPPED_ADDRESS = 0x0020;
51
+
52
+ // TURN Attribute Types (RFC 5766)
53
+ const uint16_t ATTR_CHANNEL_NUMBER = 0x000C;
54
+ const uint16_t ATTR_LIFETIME = 0x000D;
55
+ const uint16_t ATTR_BANDWIDTH = 0x0010;
56
+ const uint16_t ATTR_XOR_PEER_ADDRESS = 0x0012;
57
+ const uint16_t ATTR_DATA = 0x0013;
58
+ const uint16_t ATTR_XOR_RELAYED_ADDRESS = 0x0016;
59
+ const uint16_t ATTR_EVEN_PORT = 0x0018;
60
+ const uint16_t ATTR_REQUESTED_TRANSPORT = 0x0019;
61
+ const uint16_t ATTR_DONT_FRAGMENT = 0x001A;
62
+ const uint16_t ATTR_RESERVATION_TOKEN = 0x0022;
63
+
64
+ // ICE Attribute Types (RFC 8445)
65
+ const uint16_t ATTR_PRIORITY = 0x0024;
66
+ const uint16_t ATTR_USE_CANDIDATE = 0x0025;
67
+ const uint16_t ATTR_ICE_CONTROLLED = 0x8029;
68
+ const uint16_t ATTR_ICE_CONTROLLING = 0x802A;
69
+
70
+ // Address families
71
+ const uint8_t FAMILY_IPV4 = 0x01;
72
+ const uint8_t FAMILY_IPV6 = 0x02;
73
+
74
+ // STUN message header size
75
+ const size_t HEADER_SIZE = 20;
76
+
77
+ // Transaction ID size
78
+ const size_t TRANSACTION_ID_SIZE = 12;
79
+
80
+ // Error codes
81
+ const uint16_t ERROR_TRY_ALTERNATE = 300;
82
+ const uint16_t ERROR_BAD_REQUEST = 400;
83
+ const uint16_t ERROR_UNAUTHORIZED = 401;
84
+ const uint16_t ERROR_UNKNOWN_ATTRIBUTE = 420;
85
+ const uint16_t ERROR_STALE_NONCE = 438;
86
+ const uint16_t ERROR_SERVER_ERROR = 500;
87
+ const uint16_t ERROR_ALLOCATION_MISMATCH = 437;
88
+ const uint16_t ERROR_WRONG_CREDENTIALS = 441;
89
+ const uint16_t ERROR_UNSUPPORTED_TRANSPORT = 442;
90
+ const uint16_t ERROR_ALLOCATION_QUOTA_REACHED = 486;
91
+ const uint16_t ERROR_INSUFFICIENT_CAPACITY = 508;
92
+ }
93
+
94
+ // STUN Message Header Structure
95
+ struct StunHeader {
96
+ uint16_t message_type;
97
+ uint16_t message_length;
98
+ uint32_t magic_cookie;
99
+ uint8_t transaction_id[stun::TRANSACTION_ID_SIZE];
100
+ };
101
+
102
+ // STUN Attribute Header
103
+ struct StunAttribute {
104
+ uint16_t type;
105
+ uint16_t length;
106
+ // Value follows this header
107
+ };
108
+
109
+ // STUN Address Structure
110
+ struct StunAddress {
111
+ uint8_t family;
112
+ uint16_t port;
113
+ std::string ip;
114
+
115
+ StunAddress() : family(stun::FAMILY_IPV4), port(0) {}
116
+ StunAddress(const std::string& ip_addr, uint16_t port_num)
117
+ : family(stun::FAMILY_IPV4), port(port_num), ip(ip_addr) {}
118
+ };
119
+
120
+ // STUN Error Information
121
+ struct StunError {
122
+ uint16_t code;
123
+ std::string reason;
124
+
125
+ StunError() : code(0) {}
126
+ StunError(uint16_t error_code, const std::string& reason_phrase)
127
+ : code(error_code), reason(reason_phrase) {}
128
+ };
129
+
130
+ // STUN Message for parsing responses
131
+ struct StunMessage {
132
+ uint16_t message_type;
133
+ uint16_t message_length;
134
+ uint8_t transaction_id[stun::TRANSACTION_ID_SIZE];
135
+ std::vector<StunAttribute*> attributes;
136
+
137
+ // Parsed attribute data
138
+ StunAddress mapped_address;
139
+ StunAddress xor_mapped_address;
140
+ StunAddress source_address;
141
+ StunAddress changed_address;
142
+ StunAddress xor_relayed_address;
143
+ StunAddress xor_peer_address;
144
+ StunError error;
145
+ std::string username;
146
+ std::string realm;
147
+ std::string nonce;
148
+ std::vector<uint8_t> data;
149
+ uint32_t lifetime;
150
+ uint32_t priority;
151
+ bool use_candidate;
152
+ uint64_t ice_controlled;
153
+ uint64_t ice_controlling;
154
+
155
+ StunMessage();
156
+ ~StunMessage();
157
+ void clear();
158
+ };
159
+
160
+ // STUN Client Class with enhanced capabilities
161
+ class StunClient {
162
+ public:
163
+ StunClient();
164
+ ~StunClient();
165
+
166
+ // Basic STUN functionality
167
+ bool get_public_address(const std::string& stun_server,
168
+ int stun_port,
169
+ StunAddress& public_address,
170
+ int timeout_ms = 5000);
171
+
172
+ // Get public IP from Google STUN server
173
+ bool get_public_address_from_google(StunAddress& public_address,
174
+ int timeout_ms = 5000);
175
+
176
+ // Advanced STUN functionality for NAT detection
177
+ bool test_stun_binding(const std::string& stun_server, int stun_port,
178
+ StunAddress& mapped_addr, StunAddress& source_addr,
179
+ int timeout_ms = 5000);
180
+
181
+ bool test_change_request(const std::string& stun_server, int stun_port,
182
+ bool change_ip, bool change_port,
183
+ StunAddress& response_addr, int timeout_ms = 5000);
184
+
185
+ // ICE STUN functionality
186
+ bool send_binding_request_ice(socket_t socket, const std::string& remote_ip, uint16_t remote_port,
187
+ const std::string& username, const std::string& password,
188
+ uint32_t priority, bool controlling, uint64_t tie_breaker,
189
+ bool use_candidate = false);
190
+
191
+ bool handle_binding_request_ice(const std::vector<uint8_t>& request,
192
+ const std::string& from_ip, uint16_t from_port,
193
+ const std::string& local_username, const std::string& local_password,
194
+ std::vector<uint8_t>& response);
195
+
196
+ // Static helper functions
197
+ static std::vector<uint8_t> create_binding_request();
198
+ static std::vector<uint8_t> create_binding_request_ice(const std::string& username,
199
+ const std::string& password,
200
+ uint32_t priority,
201
+ bool controlling,
202
+ uint64_t tie_breaker,
203
+ bool use_candidate = false);
204
+
205
+ static std::vector<uint8_t> create_change_request(bool change_ip, bool change_port);
206
+ static std::vector<uint8_t> create_binding_response(const uint8_t* transaction_id,
207
+ const StunAddress& mapped_addr);
208
+ static std::vector<uint8_t> create_error_response(const uint8_t* transaction_id,
209
+ uint16_t error_code,
210
+ const std::string& reason);
211
+
212
+ static bool parse_stun_message(const std::vector<uint8_t>& data, StunMessage& message);
213
+ static bool parse_binding_response(const std::vector<uint8_t>& response,
214
+ StunAddress& mapped_address);
215
+
216
+ // TURN client functionality
217
+ bool allocate_turn_relay(const std::string& turn_server, uint16_t turn_port,
218
+ const std::string& username, const std::string& password,
219
+ StunAddress& relayed_address, uint32_t& lifetime,
220
+ int timeout_ms = 10000);
221
+
222
+ bool refresh_turn_allocation(socket_t turn_socket, uint32_t lifetime,
223
+ const std::string& username, const std::string& password,
224
+ int timeout_ms = 5000);
225
+
226
+ bool create_turn_permission(socket_t turn_socket, const StunAddress& peer_address,
227
+ const std::string& username, const std::string& password,
228
+ int timeout_ms = 5000);
229
+
230
+ bool send_turn_data(socket_t turn_socket, const std::vector<uint8_t>& data,
231
+ const StunAddress& peer_address);
232
+
233
+ // NAT traversal coordination
234
+ bool coordinate_nat_traversal(const std::string& peer_ip, uint16_t peer_port,
235
+ const std::string& coordination_data,
236
+ std::function<void(bool, const std::string&)> callback);
237
+
238
+ private:
239
+ // Helper functions
240
+ void generate_transaction_id(uint8_t* transaction_id);
241
+ bool send_stun_request(socket_t sock,
242
+ const std::string& server,
243
+ int port,
244
+ const std::vector<uint8_t>& request);
245
+ bool receive_stun_response(socket_t sock,
246
+ std::vector<uint8_t>& response,
247
+ int timeout_ms);
248
+
249
+ // Parsing helpers
250
+ static uint16_t parse_uint16(const uint8_t* data);
251
+ static uint32_t parse_uint32(const uint8_t* data);
252
+ static uint64_t parse_uint64(const uint8_t* data);
253
+ static void write_uint16(uint8_t* data, uint16_t value);
254
+ static void write_uint32(uint8_t* data, uint32_t value);
255
+ static void write_uint64(uint8_t* data, uint64_t value);
256
+
257
+ // Attribute parsing
258
+ static bool parse_address_attribute(const uint8_t* data, size_t length,
259
+ StunAddress& address, bool xor_mapped = false,
260
+ const uint8_t* transaction_id = nullptr);
261
+ static bool parse_error_attribute(const uint8_t* data, size_t length, StunError& error);
262
+ static bool parse_string_attribute(const uint8_t* data, size_t length, std::string& result);
263
+
264
+ // Attribute creation
265
+ static void add_mapped_address_attribute(std::vector<uint8_t>& message,
266
+ const StunAddress& address);
267
+ static void add_xor_mapped_address_attribute(std::vector<uint8_t>& message,
268
+ const StunAddress& address,
269
+ const uint8_t* transaction_id);
270
+ static void add_username_attribute(std::vector<uint8_t>& message, const std::string& username);
271
+ static void add_message_integrity_attribute(std::vector<uint8_t>& message,
272
+ const std::string& password);
273
+ static void add_priority_attribute(std::vector<uint8_t>& message, uint32_t priority);
274
+ static void add_ice_controlling_attribute(std::vector<uint8_t>& message, uint64_t tie_breaker);
275
+ static void add_ice_controlled_attribute(std::vector<uint8_t>& message, uint64_t tie_breaker);
276
+ static void add_use_candidate_attribute(std::vector<uint8_t>& message);
277
+ static void add_error_code_attribute(std::vector<uint8_t>& message,
278
+ uint16_t error_code, const std::string& reason);
279
+
280
+ // XOR operations for XOR-MAPPED-ADDRESS
281
+ static void xor_address(StunAddress& address, const uint8_t* transaction_id);
282
+ static void xor_address_data(uint8_t* addr_data, size_t length, const uint8_t* transaction_id);
283
+
284
+ // Message integrity calculation
285
+ static std::vector<uint8_t> calculate_hmac_sha1(const std::vector<uint8_t>& message,
286
+ const std::string& key);
287
+ static bool verify_message_integrity(const std::vector<uint8_t>& message,
288
+ const std::string& password);
289
+
290
+ // Authentication helpers
291
+ bool authenticate_stun_message(std::vector<uint8_t>& message,
292
+ const std::string& username,
293
+ const std::string& password,
294
+ const std::string& realm = "",
295
+ const std::string& nonce = "");
296
+
297
+ // TURN specific helpers
298
+ static std::vector<uint8_t> create_allocate_request(const std::string& username,
299
+ const std::string& password);
300
+ static std::vector<uint8_t> create_refresh_request(uint32_t lifetime,
301
+ const std::string& username,
302
+ const std::string& password);
303
+ static std::vector<uint8_t> create_permission_request(const StunAddress& peer_address,
304
+ const std::string& username,
305
+ const std::string& password);
306
+ static std::vector<uint8_t> create_send_indication(const std::vector<uint8_t>& data,
307
+ const StunAddress& peer_address);
308
+
309
+ bool handle_turn_error_response(const StunMessage& message,
310
+ std::string& realm, std::string& nonce);
311
+ };
312
+
313
+ // Enhanced NAT type detection
314
+ enum class NatBehavior {
315
+ UNKNOWN,
316
+ ENDPOINT_INDEPENDENT,
317
+ ADDRESS_DEPENDENT,
318
+ ADDRESS_PORT_DEPENDENT
319
+ };
320
+
321
+ struct NatTypeInfo {
322
+ bool has_nat;
323
+ NatBehavior filtering_behavior;
324
+ NatBehavior mapping_behavior;
325
+ bool preserves_port;
326
+ bool hairpin_support;
327
+ std::string description;
328
+ };
329
+
330
+ class AdvancedNatDetector {
331
+ public:
332
+ AdvancedNatDetector();
333
+ ~AdvancedNatDetector();
334
+
335
+ NatTypeInfo detect_nat_characteristics(const std::vector<std::string>& stun_servers,
336
+ int timeout_ms = 5000);
337
+
338
+ bool test_hairpin_support(const std::string& stun_server, int timeout_ms);
339
+ bool test_port_preservation(const std::vector<std::string>& stun_servers, int timeout_ms);
340
+ NatBehavior test_filtering_behavior(const std::vector<std::string>& stun_servers, int timeout_ms);
341
+ NatBehavior test_mapping_behavior(const std::vector<std::string>& stun_servers, int timeout_ms);
342
+
343
+ private:
344
+ std::unique_ptr<StunClient> stun_client_;
345
+ };
346
+
347
+ } // namespace librats
348
+
349
+ #endif // LIBRATS_STUN_H
@@ -0,0 +1,105 @@
1
+ #include "threadmanager.h"
2
+
3
+ // ThreadManager module logging macros
4
+ #define LOG_THREAD_DEBUG(message) LOG_DEBUG("thread", message)
5
+ #define LOG_THREAD_INFO(message) LOG_INFO("thread", message)
6
+ #define LOG_THREAD_WARN(message) LOG_WARN("thread", message)
7
+ #define LOG_THREAD_ERROR(message) LOG_ERROR("thread", message)
8
+
9
+ namespace librats {
10
+
11
+ ThreadManager::ThreadManager() {
12
+ LOG_THREAD_DEBUG("ThreadManager initialized");
13
+ }
14
+
15
+ ThreadManager::~ThreadManager() {
16
+ // Ensure all threads are properly cleaned up
17
+ join_all_active_threads();
18
+ LOG_THREAD_DEBUG("ThreadManager destroyed");
19
+ }
20
+
21
+ void ThreadManager::add_managed_thread(std::thread&& t, const std::string& name) {
22
+ // Check if shutdown has been requested - don't add new threads during shutdown
23
+ if (shutdown_requested_.load()) {
24
+ LOG_THREAD_WARN("Ignoring thread detach during shutdown: " << name);
25
+ // Join the thread so it can finish on its own during shutdown
26
+ if (t.joinable()) {
27
+ t.join();
28
+ }
29
+ return;
30
+ }
31
+
32
+ std::lock_guard<std::mutex> lock(active_threads_mutex_);
33
+
34
+ // Double-check after acquiring lock
35
+ if (shutdown_requested_.load()) {
36
+ LOG_THREAD_WARN("Ignoring thread detach during shutdown (double-check): " << name);
37
+ // Join the thread so it can finish on its own during shutdown
38
+ if (t.joinable()) {
39
+ t.join();
40
+ }
41
+ return;
42
+ }
43
+
44
+ active_threads_.emplace_back(std::move(t));
45
+ LOG_THREAD_DEBUG("Added managed thread: " << name << " (total: " << active_threads_.size() << ")");
46
+ }
47
+
48
+ void ThreadManager::cleanup_finished_threads() {
49
+ // we wait until all threads are finished thus mean cleanup is done
50
+ join_all_active_threads();
51
+ }
52
+
53
+ void ThreadManager::shutdown_all_threads() {
54
+ LOG_THREAD_INFO("Initiating shutdown of all background threads");
55
+
56
+ // Set shutdown flag first
57
+ shutdown_requested_.store(true);
58
+
59
+ // Notify all waiting threads to wake up immediately
60
+ notify_shutdown();
61
+ }
62
+
63
+ void ThreadManager::join_all_active_threads() {
64
+ std::vector<std::thread> threads_to_join;
65
+
66
+ // Move threads out of the container while holding the lock
67
+ {
68
+ std::lock_guard<std::mutex> lock(active_threads_mutex_);
69
+ LOG_THREAD_INFO("Waiting for " << active_threads_.size() << " managed threads to finish");
70
+
71
+ if (active_threads_.empty()) {
72
+ LOG_THREAD_INFO("No active threads to join");
73
+ return;
74
+ }
75
+
76
+ // Move all threads to local vector to avoid holding lock during join
77
+ threads_to_join = std::move(active_threads_);
78
+ active_threads_.clear();
79
+ }
80
+
81
+ // Join threads without holding the mutex
82
+ for (auto& t : threads_to_join) {
83
+ if (t.joinable()) {
84
+ try {
85
+ t.join();
86
+ } catch (const std::exception& e) {
87
+ LOG_THREAD_ERROR("Exception while joining thread: " << e.what());
88
+ }
89
+ }
90
+ }
91
+
92
+ LOG_THREAD_INFO("All managed threads have been cleaned up");
93
+ }
94
+
95
+ size_t ThreadManager::get_active_thread_count() const {
96
+ std::lock_guard<std::mutex> lock(active_threads_mutex_);
97
+ return active_threads_.size();
98
+ }
99
+
100
+ void ThreadManager::notify_shutdown() {
101
+ shutdown_cv_.notify_all();
102
+ }
103
+
104
+ } // namespace librats
105
+
@@ -0,0 +1,53 @@
1
+ #ifndef THREADMANAGER_H
2
+ #define THREADMANAGER_H
3
+
4
+ #include <thread>
5
+ #include <vector>
6
+ #include <mutex>
7
+ #include <condition_variable>
8
+ #include <atomic>
9
+ #include "logger.h"
10
+
11
+ namespace librats {
12
+
13
+ /**
14
+ * ThreadManager - Manages background threads with graceful shutdown
15
+ */
16
+ class ThreadManager {
17
+ public:
18
+ ThreadManager();
19
+ virtual ~ThreadManager();
20
+
21
+ // Add a new managed thread with optional name for debugging
22
+ void add_managed_thread(std::thread&& t, const std::string& name = "unnamed");
23
+
24
+ // Clean up finished threads (non-blocking)
25
+ void cleanup_finished_threads();
26
+
27
+ // Signal shutdown to all threads
28
+ void shutdown_all_threads();
29
+
30
+ // Wait for all active threads to finish (blocking)
31
+ void join_all_active_threads();
32
+
33
+ // Get count of active threads
34
+ size_t get_active_thread_count() const;
35
+
36
+ protected:
37
+ // Notify waiting threads of shutdown
38
+ void notify_shutdown();
39
+
40
+ // Condition variable for shutdown coordination
41
+ std::condition_variable shutdown_cv_;
42
+ std::mutex shutdown_mutex_;
43
+
44
+ private:
45
+ mutable std::mutex active_threads_mutex_;
46
+ std::vector<std::thread> active_threads_;
47
+ std::atomic<bool> shutdown_requested_{false};
48
+ };
49
+
50
+ } // namespace librats
51
+
52
+ #endif // THREADMANAGER_H
53
+