Common System 0.2.0
Common interfaces and patterns for system integration
Loading...
Searching...
No Matches
service_container.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
22#pragma once
23
26
27#include <atomic>
28#include <mutex>
29#include <shared_mutex>
30#include <unordered_map>
31#include <unordered_set>
32
33namespace kcenon::common {
34namespace di {
35
36// Forward declaration
37class service_scope;
38
67public:
76 static service_container& global();
77
81 service_container() = default;
82
86 ~service_container() override = default;
87
88 // Non-copyable, non-movable
93
94 // ===== IServiceContainer Implementation =====
95
100 std::unique_ptr<IServiceScope> create_scope() override;
101
106 std::vector<service_descriptor> registered_services() const override;
107
112 VoidResult clear() override;
113
114 // ===== Security Controls =====
115
130 void freeze();
131
137 bool is_frozen() const;
138
139protected:
140 // ===== Internal Implementation =====
141
143 std::type_index interface_type,
144 const std::string& type_name,
145 std::function<std::shared_ptr<void>(IServiceContainer&)> factory,
146 service_lifetime lifetime) override;
147
149 std::type_index interface_type,
150 const std::string& type_name,
151 std::shared_ptr<void> instance) override;
152
154 std::type_index interface_type) override;
155
156 bool is_registered_internal(std::type_index interface_type) const override;
157
158 VoidResult unregister_internal(std::type_index interface_type) override;
159
160private:
161 friend class service_scope;
162
167 std::type_index interface_type;
168 std::string type_name;
169 std::function<std::shared_ptr<void>(IServiceContainer&)> factory;
171 std::shared_ptr<void> singleton_instance;
172 bool is_instantiated = false;
173
174 service_entry(std::type_index type, std::string name,
175 std::function<std::shared_ptr<void>(IServiceContainer&)> f,
177 : interface_type(type)
178 , type_name(std::move(name))
179 , factory(std::move(f))
180 , lifetime(lt) {}
181 };
182
191 std::type_index interface_type,
192 std::unordered_map<std::type_index, std::shared_ptr<void>>* scoped_instances = nullptr);
193
197 bool is_resolving(std::type_index interface_type) const;
198
202 void push_resolution(std::type_index interface_type);
203
207 void pop_resolution(std::type_index interface_type);
208
212 std::string get_resolution_stack_string() const;
213
214 // ===== Helper Methods for Reducing Code Duplication =====
215
225 const std::string& type_name,
227 const std::string& error_message) const;
228
239 std::type_index interface_type,
240 const std::string& type_name) const;
241
249 const std::function<std::shared_ptr<void>(IServiceContainer&)>& factory);
250
251 // Service registry
252 mutable std::shared_mutex mutex_;
253 std::unordered_map<std::type_index, service_entry> services_;
254
255 // Security controls
256 std::atomic<bool> frozen_{false};
257
258 // Thread-local resolution stack for circular dependency detection
259 static thread_local std::unordered_set<std::type_index> resolution_stack_;
260 static thread_local std::vector<std::type_index> resolution_order_;
261};
262
281public:
287
291 ~service_scope() override = default;
292
293 // Non-copyable, non-movable
294 service_scope(const service_scope&) = delete;
298
299 // ===== IServiceScope Implementation =====
300
305 IServiceContainer& parent() override;
306
311 const IServiceContainer& parent() const override;
312
313 // ===== IServiceContainer Implementation =====
314
315 std::unique_ptr<IServiceScope> create_scope() override;
316 std::vector<service_descriptor> registered_services() const override;
317 VoidResult clear() override;
318
319protected:
321 std::type_index interface_type,
322 const std::string& type_name,
323 std::function<std::shared_ptr<void>(IServiceContainer&)> factory,
324 service_lifetime lifetime) override;
325
327 std::type_index interface_type,
328 const std::string& type_name,
329 std::shared_ptr<void> instance) override;
330
332 std::type_index interface_type) override;
333
334 bool is_registered_internal(std::type_index interface_type) const override;
335
336 VoidResult unregister_internal(std::type_index interface_type) override;
337
338private:
340 mutable std::shared_mutex mutex_;
341 std::unordered_map<std::type_index, std::shared_ptr<void>> scoped_instances_;
342};
343
344// ============================================================================
345// service_container Implementation
346// ============================================================================
347
348// Thread-local storage for circular dependency detection
349inline thread_local std::unordered_set<std::type_index> service_container::resolution_stack_;
350inline thread_local std::vector<std::type_index> service_container::resolution_order_;
351
353 static service_container instance;
354 return instance;
355}
356
357inline std::unique_ptr<IServiceScope> service_container::create_scope() {
358 return std::make_unique<service_scope>(*this);
359}
360
361inline std::vector<service_descriptor> service_container::registered_services() const {
362 std::shared_lock lock(mutex_);
363
364 std::vector<service_descriptor> result;
365 result.reserve(services_.size());
366
367 for (const auto& [type_index, entry] : services_) {
368 service_descriptor desc(entry.interface_type, entry.type_name, entry.lifetime);
369 desc.is_instantiated = entry.is_instantiated;
370 result.push_back(std::move(desc));
371 }
372
373 return result;
374}
375
377 if (is_frozen()) {
381 "Container is frozen"));
384 "Cannot clear services: container is frozen",
385 "di::service_container"
386 );
387 }
388
389 std::unique_lock lock(mutex_);
390 services_.clear();
391
395
396 return VoidResult::ok({});
397}
398
406
407inline bool service_container::is_frozen() const {
408 return frozen_.load(std::memory_order_acquire);
409}
410
411// ============================================================================
412// Helper Methods Implementation
413// ============================================================================
414
416 const std::string& type_name,
418 const std::string& error_message) const {
419
420 if (is_frozen()) {
422 action, type_name,
424 "Container is frozen"));
427 error_message,
428 "di::service_container"
429 );
430 }
431
432 return VoidResult::ok({});
433}
434
436 std::type_index interface_type,
437 const std::string& type_name) const {
438
439 if (services_.find(interface_type) != services_.end()) {
443 "Service already registered"));
446 "Service already registered: " + type_name,
447 "di::service_container"
448 );
449 }
450
451 return VoidResult::ok({});
452}
453
455 const std::function<std::shared_ptr<void>(IServiceContainer&)>& factory) {
456
457 try {
458 return Result<std::shared_ptr<void>>::ok(factory(*this));
459 } catch (const std::exception& e) {
462 std::string("Factory threw exception: ") + e.what(),
463 "di::service_container"
464 );
465 }
466}
467
469 std::type_index interface_type,
470 const std::string& type_name,
471 std::function<std::shared_ptr<void>(IServiceContainer&)> factory,
472 service_lifetime lifetime) {
473
474 // Check frozen state before acquiring lock
475 auto frozen_check = check_frozen_for_registration(
476 type_name,
478 "Cannot register service: container is frozen");
479 if (!frozen_check.is_ok()) {
480 return frozen_check;
481 }
482
483 std::unique_lock lock(mutex_);
484
485 // Check if already registered (must hold lock)
486 auto registered_check = check_already_registered(interface_type, type_name);
487 if (!registered_check.is_ok()) {
488 return registered_check;
489 }
490
491 services_.emplace(
492 interface_type,
493 service_entry(interface_type, type_name, std::move(factory), lifetime)
494 );
495
499
500 return VoidResult::ok({});
501}
502
504 std::type_index interface_type,
505 const std::string& type_name,
506 std::shared_ptr<void> instance) {
507
508 // Check frozen state before acquiring lock
509 auto frozen_check = check_frozen_for_registration(
510 type_name,
512 "Cannot register instance: container is frozen");
513 if (!frozen_check.is_ok()) {
514 return frozen_check;
515 }
516
517 std::unique_lock lock(mutex_);
518
519 // Check if already registered (must hold lock)
520 auto registered_check = check_already_registered(interface_type, type_name);
521 if (!registered_check.is_ok()) {
522 return registered_check;
523 }
524
525 // Create entry with instance as singleton
526 service_entry entry(
527 interface_type,
528 type_name,
529 [](IServiceContainer&) -> std::shared_ptr<void> { return nullptr; },
531 );
532 entry.singleton_instance = std::move(instance);
533 entry.is_instantiated = true;
534
535 services_.emplace(interface_type, std::move(entry));
536
540
541 return VoidResult::ok({});
542}
543
545 std::type_index interface_type) {
546 return resolve_with_detection(interface_type, nullptr);
547}
548
550 std::type_index interface_type,
551 std::unordered_map<std::type_index, std::shared_ptr<void>>* scoped_instances) {
552
553 // Check for circular dependency
554 if (is_resolving(interface_type)) {
555 std::string cycle_info = get_resolution_stack_string();
558 "Circular dependency detected: " + cycle_info,
559 "di::service_container"
560 );
561 }
562
563 // Check if service is registered
564 {
565 std::shared_lock lock(mutex_);
566 auto it = services_.find(interface_type);
567 if (it == services_.end()) {
570 "Service not registered: " + std::string(interface_type.name()),
571 "di::service_container"
572 );
573 }
574 }
575
576 // Push onto resolution stack
577 push_resolution(interface_type);
578
579 // RAII guard to pop from resolution stack
580 struct resolution_guard {
581 service_container* container;
582 std::type_index type;
583 ~resolution_guard() { container->pop_resolution(type); }
584 } guard{this, interface_type};
585
586 // Resolve based on lifetime
587 std::shared_lock read_lock(mutex_);
588 auto it = services_.find(interface_type);
589 auto& entry = it->second;
590
591 // Copy factory and lifetime before releasing lock to avoid deadlock
592 // when factory calls resolve() recursively
593 auto factory_copy = entry.factory;
594 auto lifetime = entry.lifetime;
595
596 switch (lifetime) {
598 // Double-checked locking for singleton
599 if (entry.is_instantiated) {
600 return Result<std::shared_ptr<void>>::ok(entry.singleton_instance);
601 }
602
603 read_lock.unlock();
604
605 // Create instance outside of lock to avoid deadlock
606 auto factory_result = invoke_factory_safe(factory_copy);
607 if (!factory_result.is_ok()) {
608 return factory_result;
609 }
610
611 // Now acquire write lock to store the instance
612 std::unique_lock write_lock(mutex_);
613
614 // Re-check after acquiring write lock (another thread may have created it)
615 auto it2 = services_.find(interface_type);
616 auto& entry2 = it2->second;
617 if (entry2.is_instantiated) {
618 // Another thread created it, return that instance
619 return Result<std::shared_ptr<void>>::ok(entry2.singleton_instance);
620 }
621
622 entry2.singleton_instance = std::move(factory_result.value());
623 entry2.is_instantiated = true;
624 return Result<std::shared_ptr<void>>::ok(entry2.singleton_instance);
625 }
626
628 // Create new instance each time - release lock before calling factory
629 read_lock.unlock();
630 return invoke_factory_safe(factory_copy);
631 }
632
634 // Scoped services should be resolved from a scope
635 if (scoped_instances == nullptr) {
638 "Cannot resolve scoped service from root container. Use create_scope().",
639 "di::service_container"
640 );
641 }
642
643 // Check if already instantiated in scope
644 auto scoped_it = scoped_instances->find(interface_type);
645 if (scoped_it != scoped_instances->end()) {
646 return Result<std::shared_ptr<void>>::ok(scoped_it->second);
647 }
648
649 // Create new instance for scope - release lock before calling factory
650 read_lock.unlock();
651 auto factory_result = invoke_factory_safe(factory_copy);
652 if (!factory_result.is_ok()) {
653 return factory_result;
654 }
655
656 auto instance = std::move(factory_result.value());
657 (*scoped_instances)[interface_type] = instance;
658 return Result<std::shared_ptr<void>>::ok(std::move(instance));
659 }
660
661 default:
664 "Unknown service lifetime",
665 "di::service_container"
666 );
667 }
668}
669
670inline bool service_container::is_registered_internal(std::type_index interface_type) const {
671 std::shared_lock lock(mutex_);
672 return services_.find(interface_type) != services_.end();
673}
674
675inline VoidResult service_container::unregister_internal(std::type_index interface_type) {
676 std::string type_name = interface_type.name();
677
678 if (is_frozen()) {
682 "Container is frozen"));
685 "Cannot unregister service: container is frozen",
686 "di::service_container"
687 );
688 }
689
690 std::unique_lock lock(mutex_);
691
692 auto it = services_.find(interface_type);
693 if (it == services_.end()) {
697 "Service not registered"));
700 "Service not registered",
701 "di::service_container"
702 );
703 }
704
705 services_.erase(it);
706
710
711 return VoidResult::ok({});
712}
713
714inline bool service_container::is_resolving(std::type_index interface_type) const {
715 return resolution_stack_.find(interface_type) != resolution_stack_.end();
716}
717
718inline void service_container::push_resolution(std::type_index interface_type) {
719 resolution_stack_.insert(interface_type);
720 resolution_order_.push_back(interface_type);
721}
722
723inline void service_container::pop_resolution(std::type_index interface_type) {
724 resolution_stack_.erase(interface_type);
725 if (!resolution_order_.empty() && resolution_order_.back() == interface_type) {
726 resolution_order_.pop_back();
727 }
728}
729
731 std::string result;
732 for (size_t i = 0; i < resolution_order_.size(); ++i) {
733 if (i > 0) {
734 result += " -> ";
735 }
736 result += resolution_order_[i].name();
737 }
738 return result;
739}
740
741// ============================================================================
742// service_scope Implementation
743// ============================================================================
744
746 : parent_(parent) {}
747
751
753 return parent_;
754}
755
756inline std::unique_ptr<IServiceScope> service_scope::create_scope() {
757 // Nested scopes share the same root parent
758 return std::make_unique<service_scope>(parent_);
759}
760
761inline std::vector<service_descriptor> service_scope::registered_services() const {
762 // Return parent's registered services
764}
765
767 std::unique_lock lock(mutex_);
768 scoped_instances_.clear();
769 return VoidResult::ok({});
770}
771
773 std::type_index interface_type,
774 const std::string& type_name,
775 std::function<std::shared_ptr<void>(IServiceContainer&)> factory,
776 service_lifetime lifetime) {
777 // Delegate to parent
778 return parent_.register_factory_internal(interface_type, type_name, std::move(factory), lifetime);
779}
780
782 std::type_index interface_type,
783 const std::string& type_name,
784 std::shared_ptr<void> instance) {
785 // Delegate to parent
786 return parent_.register_instance_internal(interface_type, type_name, std::move(instance));
787}
788
790 std::type_index interface_type) {
791 // Protect scoped_instances_ for thread-safe concurrent resolution.
792 // The mutex is held during the entire resolution to prevent data races
793 // when multiple threads resolve the same scoped service simultaneously.
794 std::unique_lock lock(mutex_);
795 return parent_.resolve_with_detection(interface_type, &scoped_instances_);
796}
797
798inline bool service_scope::is_registered_internal(std::type_index interface_type) const {
799 return parent_.is_registered_internal(interface_type);
800}
801
802inline VoidResult service_scope::unregister_internal(std::type_index interface_type) {
803 // Delegate to parent
804 return parent_.unregister_internal(interface_type);
805}
806
807} // namespace di
808} // namespace kcenon::common
Result type for error handling with member function support.
Definition core.cppm:165
static Result< T > ok(U &&value)
Create a successful result with value (static factory)
Definition core.h:223
Abstract interface for dependency injection containers.
Definition di.cppm:39
Scoped service container for request-level isolation.
Concrete implementation of IServiceContainer.
std::unordered_map< std::type_index, service_entry > services_
VoidResult register_factory_internal(std::type_index interface_type, const std::string &type_name, std::function< std::shared_ptr< void >(IServiceContainer &)> factory, service_lifetime lifetime) override
Internal factory registration (type-erased).
VoidResult check_already_registered(std::type_index interface_type, const std::string &type_name) const
Check if a service is already registered and log audit event if so.
static thread_local std::unordered_set< std::type_index > resolution_stack_
bool is_registered_internal(std::type_index interface_type) const override
Internal registration check (type-erased).
VoidResult unregister_internal(std::type_index interface_type) override
Internal unregistration (type-erased).
bool is_resolving(std::type_index interface_type) const
Check if currently resolving this type (circular dependency check).
std::string get_resolution_stack_string() const
Get current resolution stack as string for error messages.
Result< std::shared_ptr< void > > resolve_internal(std::type_index interface_type) override
Internal service resolution (type-erased).
Result< std::shared_ptr< void > > resolve_with_detection(std::type_index interface_type, std::unordered_map< std::type_index, std::shared_ptr< void > > *scoped_instances=nullptr)
Resolve a service with circular dependency detection.
service_container(const service_container &)=delete
VoidResult register_instance_internal(std::type_index interface_type, const std::string &type_name, std::shared_ptr< void > instance) override
Internal instance registration (type-erased).
void freeze()
Freeze the container to prevent further registrations.
service_container(service_container &&)=delete
static service_container & global()
Get the global service container instance.
bool is_frozen() const
Check if the container is frozen.
void push_resolution(std::type_index interface_type)
Push type onto resolution stack.
service_container & operator=(const service_container &)=delete
~service_container() override=default
Destructor.
std::vector< service_descriptor > registered_services() const override
Get list of all registered service descriptors.
VoidResult check_frozen_for_registration(const std::string &type_name, interfaces::registry_action action, const std::string &error_message) const
Check if container is frozen and log audit event if so.
VoidResult clear() override
Clear all registrations.
std::unique_ptr< IServiceScope > create_scope() override
Create a new service scope.
service_container & operator=(service_container &&)=delete
static thread_local std::vector< std::type_index > resolution_order_
service_container()=default
Default constructor.
Result< std::shared_ptr< void > > invoke_factory_safe(const std::function< std::shared_ptr< void >(IServiceContainer &)> &factory)
Safely invoke a factory function with exception handling.
void pop_resolution(std::type_index interface_type)
Pop type from resolution stack.
Scoped service container implementation.
service_scope & operator=(const service_scope &)=delete
std::unique_ptr< IServiceScope > create_scope() override
Create a new service scope.
~service_scope() override=default
Destructor - disposes scoped instances.
service_scope(const service_scope &)=delete
std::unordered_map< std::type_index, std::shared_ptr< void > > scoped_instances_
bool is_registered_internal(std::type_index interface_type) const override
Internal registration check (type-erased).
service_scope & operator=(service_scope &&)=delete
VoidResult unregister_internal(std::type_index interface_type) override
Internal unregistration (type-erased).
IServiceContainer & parent() override
Get the parent container.
VoidResult register_instance_internal(std::type_index interface_type, const std::string &type_name, std::shared_ptr< void > instance) override
Internal instance registration (type-erased).
VoidResult register_factory_internal(std::type_index interface_type, const std::string &type_name, std::function< std::shared_ptr< void >(IServiceContainer &)> factory, service_lifetime lifetime) override
Internal factory registration (type-erased).
Result< std::shared_ptr< void > > resolve_internal(std::type_index interface_type) override
Internal service resolution (type-erased).
service_scope(service_scope &&)=delete
std::vector< service_descriptor > registered_services() const override
Get list of all registered service descriptors.
service_scope(service_container &parent)
Construct a scope with a parent container.
VoidResult clear() override
Clear all registrations.
static void log_event(const registry_event &event)
Log a registry event.
constexpr int scoped_from_root
Scoped service resolved from root container.
constexpr int circular_dependency
Circular dependency detected during resolution.
constexpr int invalid_lifetime
Invalid service lifetime configuration.
constexpr int factory_error
Factory threw an exception during instantiation.
constexpr int service_not_registered
Service not registered in container.
constexpr int already_registered
Service already registered (duplicate registration attempt)
service_lifetime
Defines the lifetime policy for registered services.
@ singleton
Single instance shared globally.
@ scoped
Single instance within a scope.
@ transient
New instance created for each request.
constexpr int REGISTRY_FROZEN
Definition compat.h:41
registry_action
Types of registry mutation actions.
@ unregister_service
Service unregistration.
@ freeze_service_container
Freeze service container.
Core interfaces.
Definition adapter.h:21
Result< T > make_error(int code, const std::string &message, const std::string &module="")
Create an error result with code and message.
Definition utilities.h:91
VoidResult ok()
Create a successful void result.
Definition utilities.h:71
Audit logging for registry operations.
Service container interfaces for dependency injection.
Internal service registration entry.
service_entry(std::type_index type, std::string name, std::function< std::shared_ptr< void >(IServiceContainer &)> f, service_lifetime lt)
std::shared_ptr< void > singleton_instance
bool is_instantiated
std::function< std::shared_ptr< void >(IServiceContainer &)> factory
std::string type_name
service_lifetime lifetime
std::type_index interface_type
Metadata describing a registered service.
bool is_instantiated
Whether this service has been instantiated (for singletons)
Represents a single audit event for registry mutations.
static constexpr source_location current(const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE()) noexcept