1// 2// detail/impl/service_registry.ipp 3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4// 5// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6// 7// Distributed under the Boost Software License, Version 1.0. (See accompanying 8// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9// 10 11#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP 12#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP 13 14 15#include "asio/detail/config.hpp" 16#include <vector> 17#include "asio/detail/service_registry.hpp" 18#include "asio/detail/throw_exception.hpp" 19 20#include "asio/detail/push_options.hpp" 21 22namespace asio { 23namespace detail { 24 25service_registry::~service_registry() 26{ 27 // Shutdown all services. This must be done in a separate loop before the 28 // services are destroyed since the destructors of user-defined handler 29 // objects may try to access other service objects. 30 asio::io_service::service* service = first_service_; 31 while (service) 32 { 33 service->shutdown_service(); 34 service = service->next_; 35 } 36 37 // Destroy all services. 38 while (first_service_) 39 { 40 asio::io_service::service* next_service = first_service_->next_; 41 destroy(first_service_); 42 first_service_ = next_service; 43 } 44} 45 46void service_registry::notify_fork(asio::io_service::fork_event fork_ev) 47{ 48 // Make a copy of all of the services while holding the lock. We don't want 49 // to hold the lock while calling into each service, as it may try to call 50 // back into this class. 51 std::vector<asio::io_service::service*> services; 52 { 53 asio::detail::mutex::scoped_lock lock(mutex_); 54 asio::io_service::service* service = first_service_; 55 while (service) 56 { 57 services.push_back(service); 58 service = service->next_; 59 } 60 } 61 62 // If processing the fork_prepare event, we want to go in reverse order of 63 // service registration, which happens to be the existing order of the 64 // services in the vector. For the other events we want to go in the other 65 // direction. 66 std::size_t num_services = services.size(); 67 if (fork_ev == asio::io_service::fork_prepare) 68 for (std::size_t i = 0; i < num_services; ++i) 69 services[i]->fork_service(fork_ev); 70 else 71 for (std::size_t i = num_services; i > 0; --i) 72 services[i - 1]->fork_service(fork_ev); 73} 74 75void service_registry::init_key(asio::io_service::service::key& key, 76 const asio::io_service::id& id) 77{ 78 key.type_info_ = 0; 79 key.id_ = &id; 80} 81 82bool service_registry::keys_match( 83 const asio::io_service::service::key& key1, 84 const asio::io_service::service::key& key2) 85{ 86 if (key1.id_ && key2.id_) 87 if (key1.id_ == key2.id_) 88 return true; 89 if (key1.type_info_ && key2.type_info_) 90 if (*key1.type_info_ == *key2.type_info_) 91 return true; 92 return false; 93} 94 95void service_registry::destroy(asio::io_service::service* service) 96{ 97 delete service; 98} 99 100asio::io_service::service* service_registry::do_use_service( 101 const asio::io_service::service::key& key, 102 factory_type factory) 103{ 104 asio::detail::mutex::scoped_lock lock(mutex_); 105 106 // First see if there is an existing service object with the given key. 107 asio::io_service::service* service = first_service_; 108 while (service) 109 { 110 if (keys_match(service->key_, key)) 111 return service; 112 service = service->next_; 113 } 114 115 // Create a new service object. The service registry's mutex is not locked 116 // at this time to allow for nested calls into this function from the new 117 // service's constructor. 118 lock.unlock(); 119 auto_service_ptr new_service = { factory(owner_) }; 120 new_service.ptr_->key_ = key; 121 lock.lock(); 122 123 // Check that nobody else created another service object of the same type 124 // while the lock was released. 125 service = first_service_; 126 while (service) 127 { 128 if (keys_match(service->key_, key)) 129 return service; 130 service = service->next_; 131 } 132 133 // Service was successfully initialised, pass ownership to registry. 134 new_service.ptr_->next_ = first_service_; 135 first_service_ = new_service.ptr_; 136 new_service.ptr_ = 0; 137 return first_service_; 138} 139 140void service_registry::do_add_service( 141 const asio::io_service::service::key& key, 142 asio::io_service::service* new_service) 143{ 144 if (&owner_ != &new_service->get_io_service()) 145 asio::detail::throw_exception(invalid_service_owner()); 146 147 asio::detail::mutex::scoped_lock lock(mutex_); 148 149 // Check if there is an existing service object with the given key. 150 asio::io_service::service* service = first_service_; 151 while (service) 152 { 153 if (keys_match(service->key_, key)) 154 asio::detail::throw_exception(service_already_exists()); 155 service = service->next_; 156 } 157 158 // Take ownership of the service object. 159 new_service->key_ = key; 160 new_service->next_ = first_service_; 161 first_service_ = new_service; 162} 163 164bool service_registry::do_has_service( 165 const asio::io_service::service::key& key) const 166{ 167 asio::detail::mutex::scoped_lock lock(mutex_); 168 169 asio::io_service::service* service = first_service_; 170 while (service) 171 { 172 if (keys_match(service->key_, key)) 173 return true; 174 service = service->next_; 175 } 176 177 return false; 178} 179 180} // namespace detail 181} // namespace asio 182 183#include "asio/detail/pop_options.hpp" 184 185#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP 186