MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
OperationPool.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <typeindex>
4
5namespace MayaFlux::Yantra {
6
7/**
8 * @struct PooledOperationInfo
9 * @brief Metadata about a pooled operation
10 */
12 std::string name;
13 std::type_index type;
14 std::chrono::steady_clock::time_point created_at;
15 std::chrono::steady_clock::time_point last_accessed;
16 size_t access_count = 0;
17 std::unordered_map<std::string, std::any> metadata;
18
20 : type(typeid(void))
21 {
22 }
23
24 PooledOperationInfo(std::type_index t)
25 : type(t)
26 {
27 }
28};
29
30/**
31 * @class OperationPool
32 * @brief Thread-safe pool for managing named operation instances
33 *
34 * The OperationPool provides efficient storage and retrieval of operation
35 * instances by name. It includes thread safety, access tracking, and
36 * advanced query capabilities.
37 *
38 * Key Features:
39 * - Type-safe storage and retrieval
40 * - Thread-safe operations with reader/writer locks
41 * - Access tracking and statistics
42 * - Bulk operations for efficiency
43 * - Query and filtering capabilities
44 * - Operation lifecycle callbacks
45 */
47public:
48 using OperationPtr = std::shared_ptr<void>;
49 using TypePredicate = std::function<bool(std::type_index)>;
50 using NamePredicate = std::function<bool(const std::string&)>;
51
52 /**
53 * @brief Default constructor
54 */
55 OperationPool() = default;
56
57 /**
58 * @brief Add named operation to pool
59 * @tparam OpClass Operation class type
60 * @param name Unique name for the operation
61 * @param op Shared pointer to the operation
62 * @return true if added successfully, false if name already exists
63 */
64 template <typename OpClass>
65 bool add(const std::string& name, std::shared_ptr<OpClass> op)
66 {
67 if (!op) {
68 return false;
69 }
70
71 std::unique_lock lock(m_mutex);
72
73 if (m_operations.contains(name)) {
74 return false;
75 }
76
77 m_operations[name] = std::static_pointer_cast<void>(op);
78
80 info.name = name;
81 info.type = std::type_index(typeid(OpClass));
82 info.created_at = std::chrono::steady_clock::now();
83 info.last_accessed = info.created_at;
84 info.access_count = 0;
85
86 m_info[name] = std::move(info);
87
89 m_on_add_callback(name, info.type);
90 }
91
92 return true;
93 }
94
95 /**
96 * @brief Add or replace operation in pool
97 * @tparam OpClass Operation class type
98 * @param name Name for the operation
99 * @param op Shared pointer to the operation
100 */
101 template <typename OpClass>
102 void set(const std::string& name, std::shared_ptr<OpClass> op)
103 {
104 if (!op) {
105 return;
106 }
107
108 std::unique_lock lock(m_mutex);
109
110 bool replacing = m_operations.contains(name);
111
112 m_operations[name] = std::static_pointer_cast<void>(op);
113
114 auto& info = m_info[name];
115 info.name = name;
116 info.type = std::type_index(typeid(OpClass));
117
118 if (!replacing) {
119 info.created_at = std::chrono::steady_clock::now();
120 info.access_count = 0;
121 }
122 info.last_accessed = std::chrono::steady_clock::now();
123
124 if (replacing && m_on_replace_callback) {
125 m_on_replace_callback(name, info.type);
126 } else if (!replacing && m_on_add_callback) {
127 m_on_add_callback(name, info.type);
128 }
129 }
130
131 /**
132 * @brief Get operation from pool with type safety
133 * @tparam OpClass Expected operation class type
134 * @param name Name of the operation
135 * @return Shared pointer to operation, or nullptr if not found/type mismatch
136 */
137 template <typename OpClass>
138 std::shared_ptr<OpClass> get(const std::string& name)
139 {
140 std::shared_lock lock(m_mutex);
141
142 auto it = m_operations.find(name);
143 if (it == m_operations.end()) {
144 return nullptr;
145 }
146
147 auto info_it = m_info.find(name);
148 if (info_it != m_info.end()) {
149 if (info_it->second.type != std::type_index(typeid(OpClass))) {
150 return nullptr;
151 }
152
153 {
154 lock.unlock();
155 std::unique_lock write_lock(m_mutex);
156 info_it->second.last_accessed = std::chrono::steady_clock::now();
157 info_it->second.access_count++;
158 }
159 }
160
161 return std::static_pointer_cast<OpClass>(it->second);
162 }
163
164 /**
165 * @brief Try to get operation, return optional
166 * @tparam OpClass Expected operation class type
167 * @param name Name of the operation
168 * @return Optional containing the operation if found and correct type
169 */
170 template <typename OpClass>
171 std::optional<std::shared_ptr<OpClass>> try_get(const std::string& name)
172 {
173 auto op = get<OpClass>(name);
174 return op ? std::optional(op) : std::nullopt;
175 }
176
177 /**
178 * @brief Remove operation from pool
179 * @param name Name of the operation to remove
180 * @return true if removed, false if not found
181 */
182 bool remove(const std::string& name)
183 {
184 std::unique_lock lock(m_mutex);
185
186 auto it = m_operations.find(name);
187 if (it == m_operations.end()) {
188 return false;
189 }
190
191 std::type_index type = m_info[name].type;
192
193 m_operations.erase(it);
194 m_info.erase(name);
195
197 m_on_remove_callback(name, type);
198 }
199
200 return true;
201 }
202
203 /**
204 * @brief Remove all operations of a specific type
205 * @tparam OpClass Operation class type to remove
206 * @return Number of operations removed
207 */
208 template <typename OpClass>
210 {
211 std::unique_lock lock(m_mutex);
212
213 std::type_index target_type(typeid(OpClass));
214 std::vector<std::string> to_remove;
215
216 for (const auto& [name, info] : m_info) {
217 if (info.type == target_type) {
218 to_remove.push_back(name);
219 }
220 }
221
222 for (const auto& name : to_remove) {
223 m_operations.erase(name);
224 m_info.erase(name);
225
227 m_on_remove_callback(name, target_type);
228 }
229 }
230
231 return to_remove.size();
232 }
233
234 /**
235 * @brief Clear all operations from the pool
236 */
237 void clear()
238 {
239 std::unique_lock lock(m_mutex);
240 m_operations.clear();
241 m_info.clear();
242 }
243
244 /**
245 * @brief List all operation names
246 * @return Vector of operation names
247 */
248 std::vector<std::string> list_names() const
249 {
250 std::shared_lock lock(m_mutex);
251
252 std::vector<std::string> names;
253 names.reserve(m_operations.size());
254
255 for (const auto& [name, _] : m_operations) {
256 names.push_back(name);
257 }
258
259 return names;
260 }
261
262 /**
263 * @brief Get names of operations matching a type
264 * @tparam OpClass Operation class type to match
265 * @return Vector of matching operation names
266 */
267 template <typename OpClass>
268 std::vector<std::string> list_names_by_type() const
269 {
270 std::shared_lock lock(m_mutex);
271
272 std::type_index target_type(typeid(OpClass));
273 std::vector<std::string> names;
274
275 for (const auto& [name, info] : m_info) {
276 if (info.type == target_type) {
277 names.push_back(name);
278 }
279 }
280
281 return names;
282 }
283
284 /**
285 * @brief Get names matching a predicate
286 * @param predicate Function to test each name
287 * @return Vector of matching names
288 */
289 std::vector<std::string> find_names(const NamePredicate& predicate) const
290 {
291 std::shared_lock lock(m_mutex);
292
293 std::vector<std::string> names;
294
295 for (const auto& [name, _] : m_operations) {
296 if (predicate(name)) {
297 names.push_back(name);
298 }
299 }
300
301 return names;
302 }
303
304 /**
305 * @brief Check if operation exists
306 * @param name Name to check
307 * @return true if exists
308 */
309 bool has(const std::string& name) const
310 {
311 std::shared_lock lock(m_mutex);
312 return m_operations.contains(name);
313 }
314
315 /**
316 * @brief Check if any operations of a type exist
317 * @tparam OpClass Operation class type to check
318 * @return true if at least one exists
319 */
320 template <typename OpClass>
321 bool has_type() const
322 {
323 std::shared_lock lock(m_mutex);
324
325 std::type_index target_type(typeid(OpClass));
326
327 return std::any_of(m_info.begin(), m_info.end(),
328 [target_type](const auto& pair) {
329 return pair.second.type == target_type;
330 });
331 }
332
333 /**
334 * @brief Get type of named operation
335 * @param name Operation name
336 * @return Optional containing the type index if found
337 */
338 std::optional<std::type_index> get_type(const std::string& name) const
339 {
340 std::shared_lock lock(m_mutex);
341
342 auto it = m_info.find(name);
343 if (it != m_info.end()) {
344 return it->second.type;
345 }
346
347 return std::nullopt;
348 }
349
350 /**
351 * @brief Get metadata about an operation
352 * @param name Operation name
353 * @return Optional containing the operation info if found
354 */
355 std::optional<PooledOperationInfo> get_info(const std::string& name) const
356 {
357 std::shared_lock lock(m_mutex);
358
359 auto it = m_info.find(name);
360 if (it != m_info.end()) {
361 return it->second;
362 }
363
364 return std::nullopt;
365 }
366
367 /**
368 * @brief Get pool size
369 * @return Number of operations in the pool
370 */
371 size_t size() const
372 {
373 std::shared_lock lock(m_mutex);
374 return m_operations.size();
375 }
376
377 /**
378 * @brief Check if pool is empty
379 * @return true if no operations in pool
380 */
381 bool empty() const
382 {
383 std::shared_lock lock(m_mutex);
384 return m_operations.empty();
385 }
386
387 /**
388 * @brief Get statistics about pool usage
389 * @return Map of statistics
390 */
391 std::unordered_map<std::string, std::any> get_statistics() const
392 {
393 std::shared_lock lock(m_mutex);
394
395 std::unordered_map<std::string, std::any> stats;
396 stats["total_operations"] = m_operations.size();
397
398 size_t total_accesses = 0;
399 std::string most_accessed;
400 size_t max_accesses = 0;
401
402 for (const auto& [name, info] : m_info) {
403 total_accesses += info.access_count;
404 if (info.access_count > max_accesses) {
405 max_accesses = info.access_count;
406 most_accessed = name;
407 }
408 }
409
410 stats["total_accesses"] = total_accesses;
411 if (!most_accessed.empty()) {
412 stats["most_accessed_operation"] = most_accessed;
413 stats["most_accessed_count"] = max_accesses;
414 }
415
416 return stats;
417 }
418
419 /**
420 * @brief Set callback for when operations are added
421 */
422 void on_add(std::function<void(const std::string&, std::type_index)> callback)
423 {
424 std::unique_lock lock(m_mutex);
425 m_on_add_callback = std::move(callback);
426 }
427
428 /**
429 * @brief Set callback for when operations are removed
430 */
431 void on_remove(std::function<void(const std::string&, std::type_index)> callback)
432 {
433 std::unique_lock lock(m_mutex);
434 m_on_remove_callback = std::move(callback);
435 }
436
437 /**
438 * @brief Set callback for when operations are replaced
439 */
440 void on_replace(std::function<void(const std::string&, std::type_index)> callback)
441 {
442 std::unique_lock lock(m_mutex);
443 m_on_replace_callback = std::move(callback);
444 }
445
446 /**
447 * @brief Add multiple operations at once
448 * @tparam OpClass Operation class type
449 * @param operations Map of names to operations
450 * @return Number of operations successfully added
451 */
452 template <typename OpClass>
453 size_t add_batch(const std::unordered_map<std::string, std::shared_ptr<OpClass>>& operations)
454 {
455 std::unique_lock lock(m_mutex);
456
457 size_t added = 0;
458 for (const auto& [name, op] : operations) {
459 if (op && !m_operations.contains(name)) {
460 m_operations[name] = std::static_pointer_cast<void>(op);
461
463 info.name = name;
464 info.type = std::type_index(typeid(OpClass));
465 info.created_at = std::chrono::steady_clock::now();
466 info.last_accessed = info.created_at;
467 info.access_count = 0;
468
469 m_info[name] = std::move(info);
470 added++;
471
472 if (m_on_add_callback) {
473 m_on_add_callback(name, info.type);
474 }
475 }
476 }
477
478 return added;
479 }
480
481 /**
482 * @brief Remove operations matching a predicate
483 * @param predicate Function to test each name
484 * @return Number of operations removed
485 */
486 size_t remove_if(const NamePredicate& predicate)
487 {
488 std::unique_lock lock(m_mutex);
489
490 std::vector<std::string> to_remove;
491
492 for (const auto& [name, _] : m_operations) {
493 if (predicate(name)) {
494 to_remove.push_back(name);
495 }
496 }
497
498 for (const auto& name : to_remove) {
499 auto type = m_info[name].type;
500 m_operations.erase(name);
501 m_info.erase(name);
502
504 m_on_remove_callback(name, type);
505 }
506 }
507
508 return to_remove.size();
509 }
510
511private:
512 mutable std::shared_mutex m_mutex;
513 std::unordered_map<std::string, OperationPtr> m_operations;
514 std::unordered_map<std::string, PooledOperationInfo> m_info;
515
516 std::function<void(const std::string&, std::type_index)> m_on_add_callback;
517 std::function<void(const std::string&, std::type_index)> m_on_remove_callback;
518 std::function<void(const std::string&, std::type_index)> m_on_replace_callback;
519};
520
521} // namespace MayaFlux::Yantra
std::shared_ptr< void > OperationPtr
void clear()
Clear all operations from the pool.
std::optional< PooledOperationInfo > get_info(const std::string &name) const
Get metadata about an operation.
std::function< bool(std::type_index)> TypePredicate
std::function< void(const std::string &, std::type_index)> m_on_replace_callback
std::shared_ptr< OpClass > get(const std::string &name)
Get operation from pool with type safety.
std::vector< std::string > list_names() const
List all operation names.
bool remove(const std::string &name)
Remove operation from pool.
std::vector< std::string > list_names_by_type() const
Get names of operations matching a type.
void set(const std::string &name, std::shared_ptr< OpClass > op)
Add or replace operation in pool.
void on_remove(std::function< void(const std::string &, std::type_index)> callback)
Set callback for when operations are removed.
void on_replace(std::function< void(const std::string &, std::type_index)> callback)
Set callback for when operations are replaced.
bool has(const std::string &name) const
Check if operation exists.
std::function< void(const std::string &, std::type_index)> m_on_add_callback
size_t remove_by_type()
Remove all operations of a specific type.
void on_add(std::function< void(const std::string &, std::type_index)> callback)
Set callback for when operations are added.
bool has_type() const
Check if any operations of a type exist.
size_t add_batch(const std::unordered_map< std::string, std::shared_ptr< OpClass > > &operations)
Add multiple operations at once.
OperationPool()=default
Default constructor.
std::optional< std::shared_ptr< OpClass > > try_get(const std::string &name)
Try to get operation, return optional.
std::unordered_map< std::string, PooledOperationInfo > m_info
std::optional< std::type_index > get_type(const std::string &name) const
Get type of named operation.
std::unordered_map< std::string, std::any > get_statistics() const
Get statistics about pool usage.
std::unordered_map< std::string, OperationPtr > m_operations
size_t remove_if(const NamePredicate &predicate)
Remove operations matching a predicate.
bool empty() const
Check if pool is empty.
std::function< void(const std::string &, std::type_index)> m_on_remove_callback
std::function< bool(const std::string &)> NamePredicate
std::vector< std::string > find_names(const NamePredicate &predicate) const
Get names matching a predicate.
size_t size() const
Get pool size.
bool add(const std::string &name, std::shared_ptr< OpClass > op)
Add named operation to pool.
Thread-safe pool for managing named operation instances.
std::unordered_map< std::string, std::any > metadata
std::chrono::steady_clock::time_point last_accessed
std::chrono::steady_clock::time_point created_at
Metadata about a pooled operation.