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