Home | History | Annotate | Download | only in impl
      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 
     22 namespace asio {
     23 namespace detail {
     24 
     25 service_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 
     46 void 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 
     75 void 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 
     82 bool 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 
     95 void service_registry::destroy(asio::io_service::service* service)
     96 {
     97   delete service;
     98 }
     99 
    100 asio::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 
    140 void 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 
    164 bool 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