2 // detail/impl/win_iocp_handle_service.ipp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com)
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)
12 #ifndef BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
13 #define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
15 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
17 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
19 #include <boost/asio/detail/config.hpp>
21 #if defined(BOOST_ASIO_HAS_IOCP)
23 #include <boost/asio/detail/win_iocp_handle_service.hpp>
25 #include <boost/asio/detail/push_options.hpp>
31 class win_iocp_handle_service::overlapped_wrapper
35 explicit overlapped_wrapper(boost::system::error_code& ec)
42 // Create a non-signalled manual-reset event, for GetOverlappedResult.
43 hEvent = ::CreateEventW(0, TRUE, FALSE, 0);
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);
54 DWORD last_error = ::GetLastError();
55 ec = boost::system::error_code(last_error,
56 boost::asio::error::get_system_category());
64 ::CloseHandle(hEvent);
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)),
77 void win_iocp_handle_service::shutdown()
79 // Close all implementations, causing all operations to complete.
80 boost::asio::detail::mutex::scoped_lock lock(mutex_);
81 implementation_type* impl = impl_list_;
84 close_for_destruction(*impl);
89 void win_iocp_handle_service::construct(
90 win_iocp_handle_service::implementation_type& impl)
92 impl.handle_ = INVALID_HANDLE_VALUE;
93 impl.safe_cancellation_thread_id_ = 0;
95 // Insert implementation into linked list of all implementations.
96 boost::asio::detail::mutex::scoped_lock lock(mutex_);
97 impl.next_ = impl_list_;
100 impl_list_->prev_ = &impl;
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)
108 impl.handle_ = other_impl.handle_;
109 other_impl.handle_ = INVALID_HANDLE_VALUE;
111 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
112 other_impl.safe_cancellation_thread_id_ = 0;
114 // Insert implementation into linked list of all implementations.
115 boost::asio::detail::mutex::scoped_lock lock(mutex_);
116 impl.next_ = impl_list_;
119 impl_list_->prev_ = &impl;
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)
128 close_for_destruction(impl);
130 if (this != &other_service)
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_;
137 impl.prev_->next_ = impl.next_;
139 impl.next_->prev_= impl.prev_;
144 impl.handle_ = other_impl.handle_;
145 other_impl.handle_ = INVALID_HANDLE_VALUE;
147 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
148 other_impl.safe_cancellation_thread_id_ = 0;
150 if (this != &other_service)
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_;
156 if (other_service.impl_list_)
157 other_service.impl_list_->prev_ = &impl;
158 other_service.impl_list_ = &impl;
162 void win_iocp_handle_service::destroy(
163 win_iocp_handle_service::implementation_type& impl)
165 close_for_destruction(impl);
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_;
172 impl.prev_->next_ = impl.next_;
174 impl.next_->prev_= impl.prev_;
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)
185 ec = boost::asio::error::already_open;
189 if (iocp_service_.register_handle(handle, ec))
192 impl.handle_ = handle;
193 ec = boost::system::error_code();
197 boost::system::error_code win_iocp_handle_service::close(
198 win_iocp_handle_service::implementation_type& impl,
199 boost::system::error_code& ec)
203 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
204 &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
206 if (!::CloseHandle(impl.handle_))
208 DWORD last_error = ::GetLastError();
209 ec = boost::system::error_code(last_error,
210 boost::asio::error::get_system_category());
214 ec = boost::system::error_code();
217 impl.handle_ = INVALID_HANDLE_VALUE;
218 impl.safe_cancellation_thread_id_ = 0;
222 ec = boost::system::error_code();
228 boost::system::error_code win_iocp_handle_service::cancel(
229 win_iocp_handle_service::implementation_type& impl,
230 boost::system::error_code& ec)
234 ec = boost::asio::error::bad_descriptor;
238 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
239 &impl, reinterpret_cast<uintmax_t>(impl.handle_), "cancel"));
241 if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
242 ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
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))
250 DWORD last_error = ::GetLastError();
251 if (last_error == ERROR_NOT_FOUND)
253 // ERROR_NOT_FOUND means that there were no operations to be
254 // cancelled. We swallow this error to match the behaviour on other
256 ec = boost::system::error_code();
260 ec = boost::system::error_code(last_error,
261 boost::asio::error::get_system_category());
266 ec = boost::system::error_code();
269 else if (impl.safe_cancellation_thread_id_ == 0)
271 // No operations have been started, so there's nothing to cancel.
272 ec = boost::system::error_code();
274 else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
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_))
280 DWORD last_error = ::GetLastError();
281 ec = boost::system::error_code(last_error,
282 boost::asio::error::get_system_category());
286 ec = boost::system::error_code();
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;
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)
305 ec = boost::asio::error::bad_descriptor;
309 // A request to write 0 bytes on a handle is a no-op.
310 if (buffer.size() == 0)
312 ec = boost::system::error_code();
316 overlapped_wrapper overlapped(ec);
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);
329 DWORD last_error = ::GetLastError();
330 if (last_error != ERROR_IO_PENDING)
332 ec = boost::system::error_code(last_error,
333 boost::asio::error::get_system_category());
338 // Wait for the operation to complete.
339 DWORD bytes_transferred = 0;
340 ok = ::GetOverlappedResult(impl.handle_,
341 &overlapped, &bytes_transferred, TRUE);
344 DWORD last_error = ::GetLastError();
345 ec = boost::system::error_code(last_error,
346 boost::asio::error::get_system_category());
350 ec = boost::system::error_code();
351 return bytes_transferred;
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)
358 update_cancellation_thread_id(impl);
359 iocp_service_.work_started();
363 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
365 else if (buffer.size() == 0)
367 // A request to write 0 bytes on a handle is a no-op.
368 iocp_service_.on_completion(op);
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)
382 iocp_service_.on_completion(op, last_error, bytes_transferred);
386 iocp_service_.on_pending(op);
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)
397 ec = boost::asio::error::bad_descriptor;
401 // A request to read 0 bytes on a stream handle is a no-op.
402 if (buffer.size() == 0)
404 ec = boost::system::error_code();
408 overlapped_wrapper overlapped(ec);
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);
421 DWORD last_error = ::GetLastError();
422 if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA)
424 if (last_error == ERROR_HANDLE_EOF)
426 ec = boost::asio::error::eof;
430 ec = boost::system::error_code(last_error,
431 boost::asio::error::get_system_category());
437 // Wait for the operation to complete.
438 DWORD bytes_transferred = 0;
439 ok = ::GetOverlappedResult(impl.handle_,
440 &overlapped, &bytes_transferred, TRUE);
443 DWORD last_error = ::GetLastError();
444 if (last_error == ERROR_HANDLE_EOF)
446 ec = boost::asio::error::eof;
450 ec = boost::system::error_code(last_error,
451 boost::asio::error::get_system_category());
453 return (last_error == ERROR_MORE_DATA) ? bytes_transferred : 0;
456 ec = boost::system::error_code();
457 return bytes_transferred;
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)
464 update_cancellation_thread_id(impl);
465 iocp_service_.work_started();
469 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
471 else if (buffer.size() == 0)
473 // A request to read 0 bytes on a handle is a no-op.
474 iocp_service_.on_completion(op);
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)
488 iocp_service_.on_completion(op, last_error, bytes_transferred);
492 iocp_service_.on_pending(op);
497 void win_iocp_handle_service::update_cancellation_thread_id(
498 win_iocp_handle_service::implementation_type& impl)
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);
506 void win_iocp_handle_service::close_for_destruction(implementation_type& impl)
510 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
511 &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
513 ::CloseHandle(impl.handle_);
514 impl.handle_ = INVALID_HANDLE_VALUE;
515 impl.safe_cancellation_thread_id_ = 0;
519 } // namespace detail
523 #include <boost/asio/detail/pop_options.hpp>
525 #endif // defined(BOOST_ASIO_HAS_IOCP)
527 #endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP