• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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