198 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // 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
 |