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