]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // |
b32b8144 | 2 | // detail/win_iocp_io_context.hpp |
7c673cae FG |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4 | // | |
1e59de90 | 5 | // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
7c673cae FG |
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 | ||
b32b8144 FG |
11 | #ifndef BOOST_ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP |
12 | #define BOOST_ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP | |
7c673cae FG |
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 | ||
20 | #if defined(BOOST_ASIO_HAS_IOCP) | |
21 | ||
7c673cae FG |
22 | #include <boost/asio/detail/limits.hpp> |
23 | #include <boost/asio/detail/mutex.hpp> | |
24 | #include <boost/asio/detail/op_queue.hpp> | |
25 | #include <boost/asio/detail/scoped_ptr.hpp> | |
26 | #include <boost/asio/detail/socket_types.hpp> | |
27 | #include <boost/asio/detail/thread.hpp> | |
b32b8144 | 28 | #include <boost/asio/detail/thread_context.hpp> |
7c673cae FG |
29 | #include <boost/asio/detail/timer_queue_base.hpp> |
30 | #include <boost/asio/detail/timer_queue_set.hpp> | |
31 | #include <boost/asio/detail/wait_op.hpp> | |
32 | #include <boost/asio/detail/win_iocp_operation.hpp> | |
33 | #include <boost/asio/detail/win_iocp_thread_info.hpp> | |
b32b8144 | 34 | #include <boost/asio/execution_context.hpp> |
7c673cae FG |
35 | |
36 | #include <boost/asio/detail/push_options.hpp> | |
37 | ||
38 | namespace boost { | |
39 | namespace asio { | |
40 | namespace detail { | |
41 | ||
42 | class wait_op; | |
43 | ||
b32b8144 FG |
44 | class win_iocp_io_context |
45 | : public execution_context_service_base<win_iocp_io_context>, | |
46 | public thread_context | |
7c673cae FG |
47 | { |
48 | public: | |
7c673cae FG |
49 | // Constructor. Specifies a concurrency hint that is passed through to the |
50 | // underlying I/O completion port. | |
b32b8144 | 51 | BOOST_ASIO_DECL win_iocp_io_context(boost::asio::execution_context& ctx, |
92f5a8d4 TL |
52 | int concurrency_hint = -1, bool own_thread = true); |
53 | ||
54 | // Destructor. | |
55 | BOOST_ASIO_DECL ~win_iocp_io_context(); | |
7c673cae FG |
56 | |
57 | // Destroy all user-defined handler objects owned by the service. | |
b32b8144 | 58 | BOOST_ASIO_DECL void shutdown(); |
7c673cae FG |
59 | |
60 | // Initialise the task. Nothing to do here. | |
61 | void init_task() | |
62 | { | |
63 | } | |
64 | ||
65 | // Register a handle with the IO completion port. | |
66 | BOOST_ASIO_DECL boost::system::error_code register_handle( | |
67 | HANDLE handle, boost::system::error_code& ec); | |
68 | ||
69 | // Run the event loop until stopped or no more work. | |
70 | BOOST_ASIO_DECL size_t run(boost::system::error_code& ec); | |
71 | ||
72 | // Run until stopped or one operation is performed. | |
73 | BOOST_ASIO_DECL size_t run_one(boost::system::error_code& ec); | |
74 | ||
b32b8144 FG |
75 | // Run until timeout, interrupted, or one operation is performed. |
76 | BOOST_ASIO_DECL size_t wait_one(long usec, boost::system::error_code& ec); | |
77 | ||
7c673cae FG |
78 | // Poll for operations without blocking. |
79 | BOOST_ASIO_DECL size_t poll(boost::system::error_code& ec); | |
80 | ||
81 | // Poll for one operation without blocking. | |
82 | BOOST_ASIO_DECL size_t poll_one(boost::system::error_code& ec); | |
83 | ||
84 | // Stop the event processing loop. | |
85 | BOOST_ASIO_DECL void stop(); | |
86 | ||
b32b8144 | 87 | // Determine whether the io_context is stopped. |
7c673cae FG |
88 | bool stopped() const |
89 | { | |
90 | return ::InterlockedExchangeAdd(&stopped_, 0) != 0; | |
91 | } | |
92 | ||
b32b8144 FG |
93 | // Restart in preparation for a subsequent run invocation. |
94 | void restart() | |
7c673cae FG |
95 | { |
96 | ::InterlockedExchange(&stopped_, 0); | |
97 | } | |
98 | ||
99 | // Notify that some work has started. | |
100 | void work_started() | |
101 | { | |
102 | ::InterlockedIncrement(&outstanding_work_); | |
103 | } | |
104 | ||
105 | // Notify that some work has finished. | |
106 | void work_finished() | |
107 | { | |
108 | if (::InterlockedDecrement(&outstanding_work_) == 0) | |
109 | stop(); | |
110 | } | |
111 | ||
112 | // Return whether a handler can be dispatched immediately. | |
1e59de90 | 113 | BOOST_ASIO_DECL bool can_dispatch(); |
7c673cae | 114 | |
20effc67 TL |
115 | /// Capture the current exception so it can be rethrown from a run function. |
116 | BOOST_ASIO_DECL void capture_current_exception(); | |
117 | ||
7c673cae FG |
118 | // Request invocation of the given operation and return immediately. Assumes |
119 | // that work_started() has not yet been called for the operation. | |
120 | void post_immediate_completion(win_iocp_operation* op, bool) | |
121 | { | |
122 | work_started(); | |
123 | post_deferred_completion(op); | |
124 | } | |
125 | ||
126 | // Request invocation of the given operation and return immediately. Assumes | |
127 | // that work_started() was previously called for the operation. | |
128 | BOOST_ASIO_DECL void post_deferred_completion(win_iocp_operation* op); | |
129 | ||
130 | // Request invocation of the given operation and return immediately. Assumes | |
131 | // that work_started() was previously called for the operations. | |
132 | BOOST_ASIO_DECL void post_deferred_completions( | |
133 | op_queue<win_iocp_operation>& ops); | |
134 | ||
135 | // Request invocation of the given operation using the thread-private queue | |
136 | // and return immediately. Assumes that work_started() has not yet been | |
137 | // called for the operation. | |
138 | void post_private_immediate_completion(win_iocp_operation* op) | |
139 | { | |
140 | post_immediate_completion(op, false); | |
141 | } | |
142 | ||
143 | // Request invocation of the given operation using the thread-private queue | |
144 | // and return immediately. Assumes that work_started() was previously called | |
145 | // for the operation. | |
146 | void post_private_deferred_completion(win_iocp_operation* op) | |
147 | { | |
148 | post_deferred_completion(op); | |
149 | } | |
150 | ||
b32b8144 FG |
151 | // Enqueue the given operation following a failed attempt to dispatch the |
152 | // operation for immediate invocation. | |
153 | void do_dispatch(operation* op) | |
154 | { | |
155 | post_immediate_completion(op, false); | |
156 | } | |
157 | ||
158 | // Process unfinished operations as part of a shutdown operation. Assumes | |
159 | // that work_started() was previously called for the operations. | |
7c673cae FG |
160 | BOOST_ASIO_DECL void abandon_operations(op_queue<operation>& ops); |
161 | ||
162 | // Called after starting an overlapped I/O operation that did not complete | |
163 | // immediately. The caller must have already called work_started() prior to | |
164 | // starting the operation. | |
165 | BOOST_ASIO_DECL void on_pending(win_iocp_operation* op); | |
166 | ||
167 | // Called after starting an overlapped I/O operation that completed | |
168 | // immediately. The caller must have already called work_started() prior to | |
169 | // starting the operation. | |
170 | BOOST_ASIO_DECL void on_completion(win_iocp_operation* op, | |
171 | DWORD last_error = 0, DWORD bytes_transferred = 0); | |
172 | ||
173 | // Called after starting an overlapped I/O operation that completed | |
174 | // immediately. The caller must have already called work_started() prior to | |
175 | // starting the operation. | |
176 | BOOST_ASIO_DECL void on_completion(win_iocp_operation* op, | |
177 | const boost::system::error_code& ec, DWORD bytes_transferred = 0); | |
178 | ||
179 | // Add a new timer queue to the service. | |
180 | template <typename Time_Traits> | |
181 | void add_timer_queue(timer_queue<Time_Traits>& timer_queue); | |
182 | ||
183 | // Remove a timer queue from the service. | |
184 | template <typename Time_Traits> | |
185 | void remove_timer_queue(timer_queue<Time_Traits>& timer_queue); | |
186 | ||
187 | // Schedule a new operation in the given timer queue to expire at the | |
188 | // specified absolute time. | |
189 | template <typename Time_Traits> | |
190 | void schedule_timer(timer_queue<Time_Traits>& queue, | |
191 | const typename Time_Traits::time_type& time, | |
192 | typename timer_queue<Time_Traits>::per_timer_data& timer, wait_op* op); | |
193 | ||
194 | // Cancel the timer associated with the given token. Returns the number of | |
195 | // handlers that have been posted or dispatched. | |
196 | template <typename Time_Traits> | |
197 | std::size_t cancel_timer(timer_queue<Time_Traits>& queue, | |
198 | typename timer_queue<Time_Traits>::per_timer_data& timer, | |
199 | std::size_t max_cancelled = (std::numeric_limits<std::size_t>::max)()); | |
200 | ||
1e59de90 TL |
201 | // Cancel the timer operations associated with the given key. |
202 | template <typename Time_Traits> | |
203 | void cancel_timer_by_key(timer_queue<Time_Traits>& queue, | |
204 | typename timer_queue<Time_Traits>::per_timer_data* timer, | |
205 | void* cancellation_key); | |
206 | ||
b32b8144 FG |
207 | // Move the timer operations associated with the given timer. |
208 | template <typename Time_Traits> | |
209 | void move_timer(timer_queue<Time_Traits>& queue, | |
210 | typename timer_queue<Time_Traits>::per_timer_data& to, | |
211 | typename timer_queue<Time_Traits>::per_timer_data& from); | |
212 | ||
213 | // Get the concurrency hint that was used to initialise the io_context. | |
214 | int concurrency_hint() const | |
215 | { | |
216 | return concurrency_hint_; | |
217 | } | |
218 | ||
7c673cae FG |
219 | private: |
220 | #if defined(WINVER) && (WINVER < 0x0500) | |
221 | typedef DWORD dword_ptr_t; | |
222 | typedef ULONG ulong_ptr_t; | |
223 | #else // defined(WINVER) && (WINVER < 0x0500) | |
224 | typedef DWORD_PTR dword_ptr_t; | |
225 | typedef ULONG_PTR ulong_ptr_t; | |
226 | #endif // defined(WINVER) && (WINVER < 0x0500) | |
227 | ||
228 | // Dequeues at most one operation from the I/O completion port, and then | |
229 | // executes it. Returns the number of operations that were dequeued (i.e. | |
230 | // either 0 or 1). | |
20effc67 TL |
231 | BOOST_ASIO_DECL size_t do_one(DWORD msec, |
232 | win_iocp_thread_info& this_thread, boost::system::error_code& ec); | |
7c673cae FG |
233 | |
234 | // Helper to calculate the GetQueuedCompletionStatus timeout. | |
235 | BOOST_ASIO_DECL static DWORD get_gqcs_timeout(); | |
236 | ||
237 | // Helper function to add a new timer queue. | |
238 | BOOST_ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); | |
239 | ||
240 | // Helper function to remove a timer queue. | |
241 | BOOST_ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); | |
242 | ||
243 | // Called to recalculate and update the timeout. | |
244 | BOOST_ASIO_DECL void update_timeout(); | |
245 | ||
246 | // Helper class to call work_finished() on block exit. | |
247 | struct work_finished_on_block_exit; | |
248 | ||
249 | // Helper class for managing a HANDLE. | |
250 | struct auto_handle | |
251 | { | |
252 | HANDLE handle; | |
253 | auto_handle() : handle(0) {} | |
254 | ~auto_handle() { if (handle) ::CloseHandle(handle); } | |
255 | }; | |
256 | ||
257 | // The IO completion port used for queueing operations. | |
258 | auto_handle iocp_; | |
259 | ||
260 | // The count of unfinished work. | |
261 | long outstanding_work_; | |
262 | ||
263 | // Flag to indicate whether the event loop has been stopped. | |
264 | mutable long stopped_; | |
265 | ||
266 | // Flag to indicate whether there is an in-flight stop event. Every event | |
267 | // posted using PostQueuedCompletionStatus consumes non-paged pool, so to | |
268 | // avoid exhausting this resouce we limit the number of outstanding events. | |
269 | long stop_event_posted_; | |
270 | ||
271 | // Flag to indicate whether the service has been shut down. | |
272 | long shutdown_; | |
273 | ||
274 | enum | |
275 | { | |
276 | // Timeout to use with GetQueuedCompletionStatus on older versions of | |
277 | // Windows. Some versions of windows have a "bug" where a call to | |
278 | // GetQueuedCompletionStatus can appear stuck even though there are events | |
279 | // waiting on the queue. Using a timeout helps to work around the issue. | |
280 | default_gqcs_timeout = 500, | |
281 | ||
282 | // Maximum waitable timer timeout, in milliseconds. | |
283 | max_timeout_msec = 5 * 60 * 1000, | |
284 | ||
285 | // Maximum waitable timer timeout, in microseconds. | |
286 | max_timeout_usec = max_timeout_msec * 1000, | |
287 | ||
288 | // Completion key value used to wake up a thread to dispatch timers or | |
289 | // completed operations. | |
290 | wake_for_dispatch = 1, | |
291 | ||
292 | // Completion key value to indicate that an operation has posted with the | |
293 | // original last_error and bytes_transferred values stored in the fields of | |
294 | // the OVERLAPPED structure. | |
295 | overlapped_contains_result = 2 | |
296 | }; | |
297 | ||
298 | // Timeout to use with GetQueuedCompletionStatus. | |
299 | const DWORD gqcs_timeout_; | |
300 | ||
92f5a8d4 TL |
301 | // Helper class to run the scheduler in its own thread. |
302 | struct thread_function; | |
303 | friend struct thread_function; | |
304 | ||
7c673cae FG |
305 | // Function object for processing timeouts in a background thread. |
306 | struct timer_thread_function; | |
307 | friend struct timer_thread_function; | |
308 | ||
309 | // Background thread used for processing timeouts. | |
310 | scoped_ptr<thread> timer_thread_; | |
311 | ||
312 | // A waitable timer object used for waiting for timeouts. | |
313 | auto_handle waitable_timer_; | |
314 | ||
315 | // Non-zero if timers or completed operations need to be dispatched. | |
316 | long dispatch_required_; | |
317 | ||
318 | // Mutex for protecting access to the timer queues and completed operations. | |
319 | mutex dispatch_mutex_; | |
320 | ||
321 | // The timer queues. | |
322 | timer_queue_set timer_queues_; | |
323 | ||
324 | // The operations that are ready to dispatch. | |
325 | op_queue<win_iocp_operation> completed_ops_; | |
326 | ||
b32b8144 FG |
327 | // The concurrency hint used to initialise the io_context. |
328 | const int concurrency_hint_; | |
92f5a8d4 TL |
329 | |
330 | // The thread that is running the io_context. | |
331 | scoped_ptr<thread> thread_; | |
7c673cae FG |
332 | }; |
333 | ||
334 | } // namespace detail | |
335 | } // namespace asio | |
336 | } // namespace boost | |
337 | ||
338 | #include <boost/asio/detail/pop_options.hpp> | |
339 | ||
b32b8144 | 340 | #include <boost/asio/detail/impl/win_iocp_io_context.hpp> |
7c673cae | 341 | #if defined(BOOST_ASIO_HEADER_ONLY) |
b32b8144 | 342 | # include <boost/asio/detail/impl/win_iocp_io_context.ipp> |
7c673cae FG |
343 | #endif // defined(BOOST_ASIO_HEADER_ONLY) |
344 | ||
345 | #endif // defined(BOOST_ASIO_HAS_IOCP) | |
346 | ||
b32b8144 | 347 | #endif // BOOST_ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP |