]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/asio/detail/impl/win_iocp_handle_service.ipp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / boost / asio / detail / impl / win_iocp_handle_service.ipp
1 //
2 // detail/impl/win_iocp_handle_service.ipp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com)
7 //
8 // Distributed under the Boost Software License, Version 1.0. (See accompanying
9 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10 //
11
12 #ifndef BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
13 #define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
14
15 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
16 # pragma once
17 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18
19 #include <boost/asio/detail/config.hpp>
20
21 #if defined(BOOST_ASIO_HAS_IOCP)
22
23 #include <boost/asio/detail/win_iocp_handle_service.hpp>
24
25 #include <boost/asio/detail/push_options.hpp>
26
27 namespace boost {
28 namespace asio {
29 namespace detail {
30
31 class win_iocp_handle_service::overlapped_wrapper
32 : public OVERLAPPED
33 {
34 public:
35 explicit overlapped_wrapper(boost::system::error_code& ec)
36 {
37 Internal = 0;
38 InternalHigh = 0;
39 Offset = 0;
40 OffsetHigh = 0;
41
42 // Create a non-signalled manual-reset event, for GetOverlappedResult.
43 hEvent = ::CreateEventW(0, TRUE, FALSE, 0);
44 if (hEvent)
45 {
46 // As documented in GetQueuedCompletionStatus, setting the low order
47 // bit of this event prevents our synchronous writes from being treated
48 // as completion port events.
49 DWORD_PTR tmp = reinterpret_cast<DWORD_PTR>(hEvent);
50 hEvent = reinterpret_cast<HANDLE>(tmp | 1);
51 }
52 else
53 {
54 DWORD last_error = ::GetLastError();
55 ec = boost::system::error_code(last_error,
56 boost::asio::error::get_system_category());
57 }
58 }
59
60 ~overlapped_wrapper()
61 {
62 if (hEvent)
63 {
64 ::CloseHandle(hEvent);
65 }
66 }
67 };
68
69 win_iocp_handle_service::win_iocp_handle_service(execution_context& context)
70 : execution_context_service_base<win_iocp_handle_service>(context),
71 iocp_service_(boost::asio::use_service<win_iocp_io_context>(context)),
72 mutex_(),
73 impl_list_(0)
74 {
75 }
76
77 void win_iocp_handle_service::shutdown()
78 {
79 // Close all implementations, causing all operations to complete.
80 boost::asio::detail::mutex::scoped_lock lock(mutex_);
81 implementation_type* impl = impl_list_;
82 while (impl)
83 {
84 close_for_destruction(*impl);
85 impl = impl->next_;
86 }
87 }
88
89 void win_iocp_handle_service::construct(
90 win_iocp_handle_service::implementation_type& impl)
91 {
92 impl.handle_ = INVALID_HANDLE_VALUE;
93 impl.safe_cancellation_thread_id_ = 0;
94
95 // Insert implementation into linked list of all implementations.
96 boost::asio::detail::mutex::scoped_lock lock(mutex_);
97 impl.next_ = impl_list_;
98 impl.prev_ = 0;
99 if (impl_list_)
100 impl_list_->prev_ = &impl;
101 impl_list_ = &impl;
102 }
103
104 void win_iocp_handle_service::move_construct(
105 win_iocp_handle_service::implementation_type& impl,
106 win_iocp_handle_service::implementation_type& other_impl)
107 {
108 impl.handle_ = other_impl.handle_;
109 other_impl.handle_ = INVALID_HANDLE_VALUE;
110
111 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
112 other_impl.safe_cancellation_thread_id_ = 0;
113
114 // Insert implementation into linked list of all implementations.
115 boost::asio::detail::mutex::scoped_lock lock(mutex_);
116 impl.next_ = impl_list_;
117 impl.prev_ = 0;
118 if (impl_list_)
119 impl_list_->prev_ = &impl;
120 impl_list_ = &impl;
121 }
122
123 void win_iocp_handle_service::move_assign(
124 win_iocp_handle_service::implementation_type& impl,
125 win_iocp_handle_service& other_service,
126 win_iocp_handle_service::implementation_type& other_impl)
127 {
128 close_for_destruction(impl);
129
130 if (this != &other_service)
131 {
132 // Remove implementation from linked list of all implementations.
133 boost::asio::detail::mutex::scoped_lock lock(mutex_);
134 if (impl_list_ == &impl)
135 impl_list_ = impl.next_;
136 if (impl.prev_)
137 impl.prev_->next_ = impl.next_;
138 if (impl.next_)
139 impl.next_->prev_= impl.prev_;
140 impl.next_ = 0;
141 impl.prev_ = 0;
142 }
143
144 impl.handle_ = other_impl.handle_;
145 other_impl.handle_ = INVALID_HANDLE_VALUE;
146
147 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
148 other_impl.safe_cancellation_thread_id_ = 0;
149
150 if (this != &other_service)
151 {
152 // Insert implementation into linked list of all implementations.
153 boost::asio::detail::mutex::scoped_lock lock(other_service.mutex_);
154 impl.next_ = other_service.impl_list_;
155 impl.prev_ = 0;
156 if (other_service.impl_list_)
157 other_service.impl_list_->prev_ = &impl;
158 other_service.impl_list_ = &impl;
159 }
160 }
161
162 void win_iocp_handle_service::destroy(
163 win_iocp_handle_service::implementation_type& impl)
164 {
165 close_for_destruction(impl);
166
167 // Remove implementation from linked list of all implementations.
168 boost::asio::detail::mutex::scoped_lock lock(mutex_);
169 if (impl_list_ == &impl)
170 impl_list_ = impl.next_;
171 if (impl.prev_)
172 impl.prev_->next_ = impl.next_;
173 if (impl.next_)
174 impl.next_->prev_= impl.prev_;
175 impl.next_ = 0;
176 impl.prev_ = 0;
177 }
178
179 boost::system::error_code win_iocp_handle_service::assign(
180 win_iocp_handle_service::implementation_type& impl,
181 const native_handle_type& handle, boost::system::error_code& ec)
182 {
183 if (is_open(impl))
184 {
185 ec = boost::asio::error::already_open;
186 return ec;
187 }
188
189 if (iocp_service_.register_handle(handle, ec))
190 return ec;
191
192 impl.handle_ = handle;
193 ec = boost::system::error_code();
194 return ec;
195 }
196
197 boost::system::error_code win_iocp_handle_service::close(
198 win_iocp_handle_service::implementation_type& impl,
199 boost::system::error_code& ec)
200 {
201 if (is_open(impl))
202 {
203 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
204 &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
205
206 if (!::CloseHandle(impl.handle_))
207 {
208 DWORD last_error = ::GetLastError();
209 ec = boost::system::error_code(last_error,
210 boost::asio::error::get_system_category());
211 }
212 else
213 {
214 ec = boost::system::error_code();
215 }
216
217 impl.handle_ = INVALID_HANDLE_VALUE;
218 impl.safe_cancellation_thread_id_ = 0;
219 }
220 else
221 {
222 ec = boost::system::error_code();
223 }
224
225 return ec;
226 }
227
228 boost::system::error_code win_iocp_handle_service::cancel(
229 win_iocp_handle_service::implementation_type& impl,
230 boost::system::error_code& ec)
231 {
232 if (!is_open(impl))
233 {
234 ec = boost::asio::error::bad_descriptor;
235 return ec;
236 }
237
238 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
239 &impl, reinterpret_cast<uintmax_t>(impl.handle_), "cancel"));
240
241 if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
242 ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
243 {
244 // The version of Windows supports cancellation from any thread.
245 typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
246 cancel_io_ex_t cancel_io_ex = reinterpret_cast<cancel_io_ex_t>(
247 reinterpret_cast<void*>(cancel_io_ex_ptr));
248 if (!cancel_io_ex(impl.handle_, 0))
249 {
250 DWORD last_error = ::GetLastError();
251 if (last_error == ERROR_NOT_FOUND)
252 {
253 // ERROR_NOT_FOUND means that there were no operations to be
254 // cancelled. We swallow this error to match the behaviour on other
255 // platforms.
256 ec = boost::system::error_code();
257 }
258 else
259 {
260 ec = boost::system::error_code(last_error,
261 boost::asio::error::get_system_category());
262 }
263 }
264 else
265 {
266 ec = boost::system::error_code();
267 }
268 }
269 else if (impl.safe_cancellation_thread_id_ == 0)
270 {
271 // No operations have been started, so there's nothing to cancel.
272 ec = boost::system::error_code();
273 }
274 else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
275 {
276 // Asynchronous operations have been started from the current thread only,
277 // so it is safe to try to cancel them using CancelIo.
278 if (!::CancelIo(impl.handle_))
279 {
280 DWORD last_error = ::GetLastError();
281 ec = boost::system::error_code(last_error,
282 boost::asio::error::get_system_category());
283 }
284 else
285 {
286 ec = boost::system::error_code();
287 }
288 }
289 else
290 {
291 // Asynchronous operations have been started from more than one thread,
292 // so cancellation is not safe.
293 ec = boost::asio::error::operation_not_supported;
294 }
295
296 return ec;
297 }
298
299 size_t win_iocp_handle_service::do_write(
300 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
301 const boost::asio::const_buffer& buffer, boost::system::error_code& ec)
302 {
303 if (!is_open(impl))
304 {
305 ec = boost::asio::error::bad_descriptor;
306 return 0;
307 }
308
309 // A request to write 0 bytes on a handle is a no-op.
310 if (buffer.size() == 0)
311 {
312 ec = boost::system::error_code();
313 return 0;
314 }
315
316 overlapped_wrapper overlapped(ec);
317 if (ec)
318 {
319 return 0;
320 }
321
322 // Write the data.
323 overlapped.Offset = offset & 0xFFFFFFFF;
324 overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
325 BOOL ok = ::WriteFile(impl.handle_, buffer.data(),
326 static_cast<DWORD>(buffer.size()), 0, &overlapped);
327 if (!ok)
328 {
329 DWORD last_error = ::GetLastError();
330 if (last_error != ERROR_IO_PENDING)
331 {
332 ec = boost::system::error_code(last_error,
333 boost::asio::error::get_system_category());
334 return 0;
335 }
336 }
337
338 // Wait for the operation to complete.
339 DWORD bytes_transferred = 0;
340 ok = ::GetOverlappedResult(impl.handle_,
341 &overlapped, &bytes_transferred, TRUE);
342 if (!ok)
343 {
344 DWORD last_error = ::GetLastError();
345 ec = boost::system::error_code(last_error,
346 boost::asio::error::get_system_category());
347 return 0;
348 }
349
350 ec = boost::system::error_code();
351 return bytes_transferred;
352 }
353
354 void win_iocp_handle_service::start_write_op(
355 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
356 const boost::asio::const_buffer& buffer, operation* op)
357 {
358 update_cancellation_thread_id(impl);
359 iocp_service_.work_started();
360
361 if (!is_open(impl))
362 {
363 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
364 }
365 else if (buffer.size() == 0)
366 {
367 // A request to write 0 bytes on a handle is a no-op.
368 iocp_service_.on_completion(op);
369 }
370 else
371 {
372 DWORD bytes_transferred = 0;
373 op->Offset = offset & 0xFFFFFFFF;
374 op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
375 BOOL ok = ::WriteFile(impl.handle_, buffer.data(),
376 static_cast<DWORD>(buffer.size()),
377 &bytes_transferred, op);
378 DWORD last_error = ::GetLastError();
379 if (!ok && last_error != ERROR_IO_PENDING
380 && last_error != ERROR_MORE_DATA)
381 {
382 iocp_service_.on_completion(op, last_error, bytes_transferred);
383 }
384 else
385 {
386 iocp_service_.on_pending(op);
387 }
388 }
389 }
390
391 size_t win_iocp_handle_service::do_read(
392 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
393 const boost::asio::mutable_buffer& buffer, boost::system::error_code& ec)
394 {
395 if (!is_open(impl))
396 {
397 ec = boost::asio::error::bad_descriptor;
398 return 0;
399 }
400
401 // A request to read 0 bytes on a stream handle is a no-op.
402 if (buffer.size() == 0)
403 {
404 ec = boost::system::error_code();
405 return 0;
406 }
407
408 overlapped_wrapper overlapped(ec);
409 if (ec)
410 {
411 return 0;
412 }
413
414 // Read some data.
415 overlapped.Offset = offset & 0xFFFFFFFF;
416 overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
417 BOOL ok = ::ReadFile(impl.handle_, buffer.data(),
418 static_cast<DWORD>(buffer.size()), 0, &overlapped);
419 if (!ok)
420 {
421 DWORD last_error = ::GetLastError();
422 if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA)
423 {
424 if (last_error == ERROR_HANDLE_EOF)
425 {
426 ec = boost::asio::error::eof;
427 }
428 else
429 {
430 ec = boost::system::error_code(last_error,
431 boost::asio::error::get_system_category());
432 }
433 return 0;
434 }
435 }
436
437 // Wait for the operation to complete.
438 DWORD bytes_transferred = 0;
439 ok = ::GetOverlappedResult(impl.handle_,
440 &overlapped, &bytes_transferred, TRUE);
441 if (!ok)
442 {
443 DWORD last_error = ::GetLastError();
444 if (last_error == ERROR_HANDLE_EOF)
445 {
446 ec = boost::asio::error::eof;
447 }
448 else
449 {
450 ec = boost::system::error_code(last_error,
451 boost::asio::error::get_system_category());
452 }
453 return (last_error == ERROR_MORE_DATA) ? bytes_transferred : 0;
454 }
455
456 ec = boost::system::error_code();
457 return bytes_transferred;
458 }
459
460 void win_iocp_handle_service::start_read_op(
461 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
462 const boost::asio::mutable_buffer& buffer, operation* op)
463 {
464 update_cancellation_thread_id(impl);
465 iocp_service_.work_started();
466
467 if (!is_open(impl))
468 {
469 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
470 }
471 else if (buffer.size() == 0)
472 {
473 // A request to read 0 bytes on a handle is a no-op.
474 iocp_service_.on_completion(op);
475 }
476 else
477 {
478 DWORD bytes_transferred = 0;
479 op->Offset = offset & 0xFFFFFFFF;
480 op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
481 BOOL ok = ::ReadFile(impl.handle_, buffer.data(),
482 static_cast<DWORD>(buffer.size()),
483 &bytes_transferred, op);
484 DWORD last_error = ::GetLastError();
485 if (!ok && last_error != ERROR_IO_PENDING
486 && last_error != ERROR_MORE_DATA)
487 {
488 iocp_service_.on_completion(op, last_error, bytes_transferred);
489 }
490 else
491 {
492 iocp_service_.on_pending(op);
493 }
494 }
495 }
496
497 void win_iocp_handle_service::update_cancellation_thread_id(
498 win_iocp_handle_service::implementation_type& impl)
499 {
500 if (impl.safe_cancellation_thread_id_ == 0)
501 impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
502 else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
503 impl.safe_cancellation_thread_id_ = ~DWORD(0);
504 }
505
506 void win_iocp_handle_service::close_for_destruction(implementation_type& impl)
507 {
508 if (is_open(impl))
509 {
510 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
511 &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
512
513 ::CloseHandle(impl.handle_);
514 impl.handle_ = INVALID_HANDLE_VALUE;
515 impl.safe_cancellation_thread_id_ = 0;
516 }
517 }
518
519 } // namespace detail
520 } // namespace asio
521 } // namespace boost
522
523 #include <boost/asio/detail/pop_options.hpp>
524
525 #endif // defined(BOOST_ASIO_HAS_IOCP)
526
527 #endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP