1 | // |
2 | // execution_context.hpp |
3 | // ~~~~~~~~~~~~~~~~~~~~~ |
4 | // |
5 | // Copyright (c) 2003-2024 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_EXECUTION_CONTEXT_HPP |
12 | #define BOOST_ASIO_EXECUTION_CONTEXT_HPP |
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 <cstddef> |
20 | #include <stdexcept> |
21 | #include <typeinfo> |
22 | #include <boost/asio/detail/noncopyable.hpp> |
23 | |
24 | #include <boost/asio/detail/push_options.hpp> |
25 | |
26 | namespace boost { |
27 | namespace asio { |
28 | |
29 | class execution_context; |
30 | class io_context; |
31 | |
32 | #if !defined(GENERATING_DOCUMENTATION) |
33 | template <typename Service> Service& use_service(execution_context&); |
34 | template <typename Service> Service& use_service(io_context&); |
35 | template <typename Service> void add_service(execution_context&, Service*); |
36 | template <typename Service> bool has_service(execution_context&); |
37 | #endif // !defined(GENERATING_DOCUMENTATION) |
38 | |
39 | namespace detail { class service_registry; } |
40 | |
41 | /// A context for function object execution. |
42 | /** |
43 | * An execution context represents a place where function objects will be |
44 | * executed. An @c io_context is an example of an execution context. |
45 | * |
46 | * @par The execution_context class and services |
47 | * |
48 | * Class execution_context implements an extensible, type-safe, polymorphic set |
49 | * of services, indexed by service type. |
50 | * |
51 | * Services exist to manage the resources that are shared across an execution |
52 | * context. For example, timers may be implemented in terms of a single timer |
53 | * queue, and this queue would be stored in a service. |
54 | * |
55 | * Access to the services of an execution_context is via three function |
56 | * templates, use_service(), add_service() and has_service(). |
57 | * |
58 | * In a call to @c use_service<Service>(), the type argument chooses a service, |
59 | * making available all members of the named type. If @c Service is not present |
60 | * in an execution_context, an object of type @c Service is created and added |
61 | * to the execution_context. A C++ program can check if an execution_context |
62 | * implements a particular service with the function template @c |
63 | * has_service<Service>(). |
64 | * |
65 | * Service objects may be explicitly added to an execution_context using the |
66 | * function template @c add_service<Service>(). If the @c Service is already |
67 | * present, the service_already_exists exception is thrown. If the owner of the |
68 | * service is not the same object as the execution_context parameter, the |
69 | * invalid_service_owner exception is thrown. |
70 | * |
71 | * Once a service reference is obtained from an execution_context object by |
72 | * calling use_service(), that reference remains usable as long as the owning |
73 | * execution_context object exists. |
74 | * |
75 | * All service implementations have execution_context::service as a public base |
76 | * class. Custom services may be implemented by deriving from this class and |
77 | * then added to an execution_context using the facilities described above. |
78 | * |
79 | * @par The execution_context as a base class |
80 | * |
81 | * Class execution_context may be used only as a base class for concrete |
82 | * execution context types. The @c io_context is an example of such a derived |
83 | * type. |
84 | * |
85 | * On destruction, a class that is derived from execution_context must perform |
86 | * <tt>execution_context::shutdown()</tt> followed by |
87 | * <tt>execution_context::destroy()</tt>. |
88 | * |
89 | * This destruction sequence permits programs to simplify their resource |
90 | * management by using @c shared_ptr<>. Where an object's lifetime is tied to |
91 | * the lifetime of a connection (or some other sequence of asynchronous |
92 | * operations), a @c shared_ptr to the object would be bound into the handlers |
93 | * for all asynchronous operations associated with it. This works as follows: |
94 | * |
95 | * @li When a single connection ends, all associated asynchronous operations |
96 | * complete. The corresponding handler objects are destroyed, and all @c |
97 | * shared_ptr references to the objects are destroyed. |
98 | * |
99 | * @li To shut down the whole program, the io_context function stop() is called |
100 | * to terminate any run() calls as soon as possible. The io_context destructor |
101 | * calls @c shutdown() and @c destroy() to destroy all pending handlers, |
102 | * causing all @c shared_ptr references to all connection objects to be |
103 | * destroyed. |
104 | */ |
105 | class execution_context |
106 | : private noncopyable |
107 | { |
108 | public: |
109 | class id; |
110 | class service; |
111 | |
112 | public: |
113 | /// Constructor. |
114 | BOOST_ASIO_DECL execution_context(); |
115 | |
116 | /// Destructor. |
117 | BOOST_ASIO_DECL ~execution_context(); |
118 | |
119 | protected: |
120 | /// Shuts down all services in the context. |
121 | /** |
122 | * This function is implemented as follows: |
123 | * |
124 | * @li For each service object @c svc in the execution_context set, in |
125 | * reverse order of the beginning of service object lifetime, performs @c |
126 | * svc->shutdown(). |
127 | */ |
128 | BOOST_ASIO_DECL void shutdown(); |
129 | |
130 | /// Destroys all services in the context. |
131 | /** |
132 | * This function is implemented as follows: |
133 | * |
134 | * @li For each service object @c svc in the execution_context set, in |
135 | * reverse order * of the beginning of service object lifetime, performs |
136 | * <tt>delete static_cast<execution_context::service*>(svc)</tt>. |
137 | */ |
138 | BOOST_ASIO_DECL void destroy(); |
139 | |
140 | public: |
141 | /// Fork-related event notifications. |
142 | enum fork_event |
143 | { |
144 | /// Notify the context that the process is about to fork. |
145 | fork_prepare, |
146 | |
147 | /// Notify the context that the process has forked and is the parent. |
148 | fork_parent, |
149 | |
150 | /// Notify the context that the process has forked and is the child. |
151 | fork_child |
152 | }; |
153 | |
154 | /// Notify the execution_context of a fork-related event. |
155 | /** |
156 | * This function is used to inform the execution_context that the process is |
157 | * about to fork, or has just forked. This allows the execution_context, and |
158 | * the services it contains, to perform any necessary housekeeping to ensure |
159 | * correct operation following a fork. |
160 | * |
161 | * This function must not be called while any other execution_context |
162 | * function, or any function associated with the execution_context's derived |
163 | * class, is being called in another thread. It is, however, safe to call |
164 | * this function from within a completion handler, provided no other thread |
165 | * is accessing the execution_context or its derived class. |
166 | * |
167 | * @param event A fork-related event. |
168 | * |
169 | * @throws boost::system::system_error Thrown on failure. If the notification |
170 | * fails the execution_context object should no longer be used and should be |
171 | * destroyed. |
172 | * |
173 | * @par Example |
174 | * The following code illustrates how to incorporate the notify_fork() |
175 | * function: |
176 | * @code my_execution_context.notify_fork(execution_context::fork_prepare); |
177 | * if (fork() == 0) |
178 | * { |
179 | * // This is the child process. |
180 | * my_execution_context.notify_fork(execution_context::fork_child); |
181 | * } |
182 | * else |
183 | * { |
184 | * // This is the parent process. |
185 | * my_execution_context.notify_fork(execution_context::fork_parent); |
186 | * } @endcode |
187 | * |
188 | * @note For each service object @c svc in the execution_context set, |
189 | * performs <tt>svc->notify_fork();</tt>. When processing the fork_prepare |
190 | * event, services are visited in reverse order of the beginning of service |
191 | * object lifetime. Otherwise, services are visited in order of the beginning |
192 | * of service object lifetime. |
193 | */ |
194 | BOOST_ASIO_DECL void notify_fork(fork_event event); |
195 | |
196 | /// Obtain the service object corresponding to the given type. |
197 | /** |
198 | * This function is used to locate a service object that corresponds to the |
199 | * given service type. If there is no existing implementation of the service, |
200 | * then the execution_context will create a new instance of the service. |
201 | * |
202 | * @param e The execution_context object that owns the service. |
203 | * |
204 | * @return The service interface implementing the specified service type. |
205 | * Ownership of the service interface is not transferred to the caller. |
206 | */ |
207 | template <typename Service> |
208 | friend Service& use_service(execution_context& e); |
209 | |
210 | /// Obtain the service object corresponding to the given type. |
211 | /** |
212 | * This function is used to locate a service object that corresponds to the |
213 | * given service type. If there is no existing implementation of the service, |
214 | * then the io_context will create a new instance of the service. |
215 | * |
216 | * @param ioc The io_context object that owns the service. |
217 | * |
218 | * @return The service interface implementing the specified service type. |
219 | * Ownership of the service interface is not transferred to the caller. |
220 | * |
221 | * @note This overload is preserved for backwards compatibility with services |
222 | * that inherit from io_context::service. |
223 | */ |
224 | template <typename Service> |
225 | friend Service& use_service(io_context& ioc); |
226 | |
227 | /// Creates a service object and adds it to the execution_context. |
228 | /** |
229 | * This function is used to add a service to the execution_context. |
230 | * |
231 | * @param e The execution_context object that owns the service. |
232 | * |
233 | * @param args Zero or more arguments to be passed to the service |
234 | * constructor. |
235 | * |
236 | * @throws boost::asio::service_already_exists Thrown if a service of the |
237 | * given type is already present in the execution_context. |
238 | */ |
239 | template <typename Service, typename... Args> |
240 | friend Service& make_service(execution_context& e, Args&&... args); |
241 | |
242 | /// (Deprecated: Use make_service().) Add a service object to the |
243 | /// execution_context. |
244 | /** |
245 | * This function is used to add a service to the execution_context. |
246 | * |
247 | * @param e The execution_context object that owns the service. |
248 | * |
249 | * @param svc The service object. On success, ownership of the service object |
250 | * is transferred to the execution_context. When the execution_context object |
251 | * is destroyed, it will destroy the service object by performing: @code |
252 | * delete static_cast<execution_context::service*>(svc) @endcode |
253 | * |
254 | * @throws boost::asio::service_already_exists Thrown if a service of the |
255 | * given type is already present in the execution_context. |
256 | * |
257 | * @throws boost::asio::invalid_service_owner Thrown if the service's owning |
258 | * execution_context is not the execution_context object specified by the |
259 | * @c e parameter. |
260 | */ |
261 | template <typename Service> |
262 | friend void add_service(execution_context& e, Service* svc); |
263 | |
264 | /// Determine if an execution_context contains a specified service type. |
265 | /** |
266 | * This function is used to determine whether the execution_context contains a |
267 | * service object corresponding to the given service type. |
268 | * |
269 | * @param e The execution_context object that owns the service. |
270 | * |
271 | * @return A boolean indicating whether the execution_context contains the |
272 | * service. |
273 | */ |
274 | template <typename Service> |
275 | friend bool has_service(execution_context& e); |
276 | |
277 | private: |
278 | // The service registry. |
279 | boost::asio::detail::service_registry* service_registry_; |
280 | }; |
281 | |
282 | /// Class used to uniquely identify a service. |
283 | class execution_context::id |
284 | : private noncopyable |
285 | { |
286 | public: |
287 | /// Constructor. |
288 | id() {} |
289 | }; |
290 | |
291 | /// Base class for all io_context services. |
292 | class execution_context::service |
293 | : private noncopyable |
294 | { |
295 | public: |
296 | /// Get the context object that owns the service. |
297 | execution_context& context(); |
298 | |
299 | protected: |
300 | /// Constructor. |
301 | /** |
302 | * @param owner The execution_context object that owns the service. |
303 | */ |
304 | BOOST_ASIO_DECL service(execution_context& owner); |
305 | |
306 | /// Destructor. |
307 | BOOST_ASIO_DECL virtual ~service(); |
308 | |
309 | private: |
310 | /// Destroy all user-defined handler objects owned by the service. |
311 | virtual void shutdown() = 0; |
312 | |
313 | /// Handle notification of a fork-related event to perform any necessary |
314 | /// housekeeping. |
315 | /** |
316 | * This function is not a pure virtual so that services only have to |
317 | * implement it if necessary. The default implementation does nothing. |
318 | */ |
319 | BOOST_ASIO_DECL virtual void notify_fork( |
320 | execution_context::fork_event event); |
321 | |
322 | friend class boost::asio::detail::service_registry; |
323 | struct key |
324 | { |
325 | key() : type_info_(0), id_(0) {} |
326 | const std::type_info* type_info_; |
327 | const execution_context::id* id_; |
328 | } key_; |
329 | |
330 | execution_context& owner_; |
331 | service* next_; |
332 | }; |
333 | |
334 | /// Exception thrown when trying to add a duplicate service to an |
335 | /// execution_context. |
336 | class service_already_exists |
337 | : public std::logic_error |
338 | { |
339 | public: |
340 | BOOST_ASIO_DECL service_already_exists(); |
341 | }; |
342 | |
343 | /// Exception thrown when trying to add a service object to an |
344 | /// execution_context where the service has a different owner. |
345 | class invalid_service_owner |
346 | : public std::logic_error |
347 | { |
348 | public: |
349 | BOOST_ASIO_DECL invalid_service_owner(); |
350 | }; |
351 | |
352 | namespace detail { |
353 | |
354 | // Special derived service id type to keep classes header-file only. |
355 | template <typename Type> |
356 | class service_id |
357 | : public execution_context::id |
358 | { |
359 | }; |
360 | |
361 | // Special service base class to keep classes header-file only. |
362 | template <typename Type> |
363 | class execution_context_service_base |
364 | : public execution_context::service |
365 | { |
366 | public: |
367 | static service_id<Type> id; |
368 | |
369 | // Constructor. |
370 | execution_context_service_base(execution_context& e) |
371 | : execution_context::service(e) |
372 | { |
373 | } |
374 | }; |
375 | |
376 | template <typename Type> |
377 | service_id<Type> execution_context_service_base<Type>::id; |
378 | |
379 | } // namespace detail |
380 | } // namespace asio |
381 | } // namespace boost |
382 | |
383 | #include <boost/asio/detail/pop_options.hpp> |
384 | |
385 | #include <boost/asio/impl/execution_context.hpp> |
386 | #if defined(BOOST_ASIO_HEADER_ONLY) |
387 | # include <boost/asio/impl/execution_context.ipp> |
388 | #endif // defined(BOOST_ASIO_HEADER_ONLY) |
389 | |
390 | #endif // BOOST_ASIO_EXECUTION_CONTEXT_HPP |
391 | |