198 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
		
		
			
		
	
	
			198 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
|  | // | ||
|  | // detail/impl/service_registry.ipp | ||
|  | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|  | // | ||
|  | // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com) | ||
|  | // | ||
|  | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
|  | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|  | // | ||
|  | 
 | ||
|  | #ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP | ||
|  | #define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP | ||
|  | 
 | ||
|  | #if defined(_MSC_VER) && (_MSC_VER >= 1200) | ||
|  | # pragma once | ||
|  | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | ||
|  | 
 | ||
|  | #include "asio/detail/config.hpp" | ||
|  | #include <vector> | ||
|  | #include "asio/detail/service_registry.hpp" | ||
|  | #include "asio/detail/throw_exception.hpp" | ||
|  | 
 | ||
|  | #include "asio/detail/push_options.hpp" | ||
|  | 
 | ||
|  | namespace asio { | ||
|  | namespace detail { | ||
|  | 
 | ||
|  | service_registry::service_registry(execution_context& owner) | ||
|  |   : owner_(owner), | ||
|  |     first_service_(0) | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | service_registry::~service_registry() | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | void service_registry::shutdown_services() | ||
|  | { | ||
|  |   execution_context::service* service = first_service_; | ||
|  |   while (service) | ||
|  |   { | ||
|  |     service->shutdown(); | ||
|  |     service = service->next_; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | void service_registry::destroy_services() | ||
|  | { | ||
|  |   while (first_service_) | ||
|  |   { | ||
|  |     execution_context::service* next_service = first_service_->next_; | ||
|  |     destroy(first_service_); | ||
|  |     first_service_ = next_service; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | void service_registry::notify_fork(execution_context::fork_event fork_ev) | ||
|  | { | ||
|  |   // Make a copy of all of the services while holding the lock. We don't want | ||
|  |   // to hold the lock while calling into each service, as it may try to call | ||
|  |   // back into this class. | ||
|  |   std::vector<execution_context::service*> services; | ||
|  |   { | ||
|  |     asio::detail::mutex::scoped_lock lock(mutex_); | ||
|  |     execution_context::service* service = first_service_; | ||
|  |     while (service) | ||
|  |     { | ||
|  |       services.push_back(service); | ||
|  |       service = service->next_; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   // If processing the fork_prepare event, we want to go in reverse order of | ||
|  |   // service registration, which happens to be the existing order of the | ||
|  |   // services in the vector. For the other events we want to go in the other | ||
|  |   // direction. | ||
|  |   std::size_t num_services = services.size(); | ||
|  |   if (fork_ev == execution_context::fork_prepare) | ||
|  |     for (std::size_t i = 0; i < num_services; ++i) | ||
|  |       services[i]->notify_fork(fork_ev); | ||
|  |   else | ||
|  |     for (std::size_t i = num_services; i > 0; --i) | ||
|  |       services[i - 1]->notify_fork(fork_ev); | ||
|  | } | ||
|  | 
 | ||
|  | void service_registry::init_key_from_id(execution_context::service::key& key, | ||
|  |     const execution_context::id& id) | ||
|  | { | ||
|  |   key.type_info_ = 0; | ||
|  |   key.id_ = &id; | ||
|  | } | ||
|  | 
 | ||
|  | bool service_registry::keys_match( | ||
|  |     const execution_context::service::key& key1, | ||
|  |     const execution_context::service::key& key2) | ||
|  | { | ||
|  |   if (key1.id_ && key2.id_) | ||
|  |     if (key1.id_ == key2.id_) | ||
|  |       return true; | ||
|  |   if (key1.type_info_ && key2.type_info_) | ||
|  |     if (*key1.type_info_ == *key2.type_info_) | ||
|  |       return true; | ||
|  |   return false; | ||
|  | } | ||
|  | 
 | ||
|  | void service_registry::destroy(execution_context::service* service) | ||
|  | { | ||
|  |   delete service; | ||
|  | } | ||
|  | 
 | ||
|  | execution_context::service* service_registry::do_use_service( | ||
|  |     const execution_context::service::key& key, | ||
|  |     factory_type factory, void* owner) | ||
|  | { | ||
|  |   asio::detail::mutex::scoped_lock lock(mutex_); | ||
|  | 
 | ||
|  |   // First see if there is an existing service object with the given key. | ||
|  |   execution_context::service* service = first_service_; | ||
|  |   while (service) | ||
|  |   { | ||
|  |     if (keys_match(service->key_, key)) | ||
|  |       return service; | ||
|  |     service = service->next_; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Create a new service object. The service registry's mutex is not locked | ||
|  |   // at this time to allow for nested calls into this function from the new | ||
|  |   // service's constructor. | ||
|  |   lock.unlock(); | ||
|  |   auto_service_ptr new_service = { factory(owner) }; | ||
|  |   new_service.ptr_->key_ = key; | ||
|  |   lock.lock(); | ||
|  | 
 | ||
|  |   // Check that nobody else created another service object of the same type | ||
|  |   // while the lock was released. | ||
|  |   service = first_service_; | ||
|  |   while (service) | ||
|  |   { | ||
|  |     if (keys_match(service->key_, key)) | ||
|  |       return service; | ||
|  |     service = service->next_; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Service was successfully initialised, pass ownership to registry. | ||
|  |   new_service.ptr_->next_ = first_service_; | ||
|  |   first_service_ = new_service.ptr_; | ||
|  |   new_service.ptr_ = 0; | ||
|  |   return first_service_; | ||
|  | } | ||
|  | 
 | ||
|  | void service_registry::do_add_service( | ||
|  |     const execution_context::service::key& key, | ||
|  |     execution_context::service* new_service) | ||
|  | { | ||
|  |   if (&owner_ != &new_service->context()) | ||
|  |     asio::detail::throw_exception(invalid_service_owner()); | ||
|  | 
 | ||
|  |   asio::detail::mutex::scoped_lock lock(mutex_); | ||
|  | 
 | ||
|  |   // Check if there is an existing service object with the given key. | ||
|  |   execution_context::service* service = first_service_; | ||
|  |   while (service) | ||
|  |   { | ||
|  |     if (keys_match(service->key_, key)) | ||
|  |       asio::detail::throw_exception(service_already_exists()); | ||
|  |     service = service->next_; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Take ownership of the service object. | ||
|  |   new_service->key_ = key; | ||
|  |   new_service->next_ = first_service_; | ||
|  |   first_service_ = new_service; | ||
|  | } | ||
|  | 
 | ||
|  | bool service_registry::do_has_service( | ||
|  |     const execution_context::service::key& key) const | ||
|  | { | ||
|  |   asio::detail::mutex::scoped_lock lock(mutex_); | ||
|  | 
 | ||
|  |   execution_context::service* service = first_service_; | ||
|  |   while (service) | ||
|  |   { | ||
|  |     if (keys_match(service->key_, key)) | ||
|  |       return true; | ||
|  |     service = service->next_; | ||
|  |   } | ||
|  | 
 | ||
|  |   return false; | ||
|  | } | ||
|  | 
 | ||
|  | } // namespace detail | ||
|  | } // namespace asio | ||
|  | 
 | ||
|  | #include "asio/detail/pop_options.hpp" | ||
|  | 
 | ||
|  | #endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP |