]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // detail/impl/service_registry.ipp | |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
4 | // | |
5 | // Copyright (c) 2003-2016 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(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(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(fork_ev); | |
74 | else | |
75 | for (std::size_t i = num_services; i > 0; --i) | |
76 | services[i - 1]->fork_service(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(service->key_, 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 = { 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(service->key_, 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(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(service->key_, key)) | |
158 | boost::asio::detail::throw_exception(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(service->key_, 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 |