2 // detail/impl/win_iocp_handle_service.ipp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 // Copyright (c) 2003-2016 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(
70 boost::asio::io_service& io_service)
71 : iocp_service_(boost::asio::use_service<win_iocp_io_service>(io_service)),
77 void win_iocp_handle_service::shutdown_service()
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(("handle", &impl, "close"));
205 if (!::CloseHandle(impl.handle_))
207 DWORD last_error = ::GetLastError();
208 ec = boost::system::error_code(last_error,
209 boost::asio::error::get_system_category());
213 ec = boost::system::error_code();
216 impl.handle_ = INVALID_HANDLE_VALUE;
217 impl.safe_cancellation_thread_id_ = 0;
221 ec = boost::system::error_code();
227 boost::system::error_code win_iocp_handle_service::cancel(
228 win_iocp_handle_service::implementation_type& impl,
229 boost::system::error_code& ec)
233 ec = boost::asio::error::bad_descriptor;
237 BOOST_ASIO_HANDLER_OPERATION(("handle", &impl, "cancel"));
239 if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
240 ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
242 // The version of Windows supports cancellation from any thread.
243 typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
244 cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
245 if (!cancel_io_ex(impl.handle_, 0))
247 DWORD last_error = ::GetLastError();
248 if (last_error == ERROR_NOT_FOUND)
250 // ERROR_NOT_FOUND means that there were no operations to be
251 // cancelled. We swallow this error to match the behaviour on other
253 ec = boost::system::error_code();
257 ec = boost::system::error_code(last_error,
258 boost::asio::error::get_system_category());
263 ec = boost::system::error_code();
266 else if (impl.safe_cancellation_thread_id_ == 0)
268 // No operations have been started, so there's nothing to cancel.
269 ec = boost::system::error_code();
271 else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
273 // Asynchronous operations have been started from the current thread only,
274 // so it is safe to try to cancel them using CancelIo.
275 if (!::CancelIo(impl.handle_))
277 DWORD last_error = ::GetLastError();
278 ec = boost::system::error_code(last_error,
279 boost::asio::error::get_system_category());
283 ec = boost::system::error_code();
288 // Asynchronous operations have been started from more than one thread,
289 // so cancellation is not safe.
290 ec = boost::asio::error::operation_not_supported;
296 size_t win_iocp_handle_service::do_write(
297 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
298 const boost::asio::const_buffer& buffer, boost::system::error_code& ec)
302 ec = boost::asio::error::bad_descriptor;
306 // A request to write 0 bytes on a handle is a no-op.
307 if (boost::asio::buffer_size(buffer) == 0)
309 ec = boost::system::error_code();
313 overlapped_wrapper overlapped(ec);
320 overlapped.Offset = offset & 0xFFFFFFFF;
321 overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
322 BOOL ok = ::WriteFile(impl.handle_,
323 boost::asio::buffer_cast<LPCVOID>(buffer),
324 static_cast<DWORD>(boost::asio::buffer_size(buffer)), 0, &overlapped);
327 DWORD last_error = ::GetLastError();
328 if (last_error != ERROR_IO_PENDING)
330 ec = boost::system::error_code(last_error,
331 boost::asio::error::get_system_category());
336 // Wait for the operation to complete.
337 DWORD bytes_transferred = 0;
338 ok = ::GetOverlappedResult(impl.handle_,
339 &overlapped, &bytes_transferred, TRUE);
342 DWORD last_error = ::GetLastError();
343 ec = boost::system::error_code(last_error,
344 boost::asio::error::get_system_category());
348 ec = boost::system::error_code();
349 return bytes_transferred;
352 void win_iocp_handle_service::start_write_op(
353 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
354 const boost::asio::const_buffer& buffer, operation* op)
356 update_cancellation_thread_id(impl);
357 iocp_service_.work_started();
361 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
363 else if (boost::asio::buffer_size(buffer) == 0)
365 // A request to write 0 bytes on a handle is a no-op.
366 iocp_service_.on_completion(op);
370 DWORD bytes_transferred = 0;
371 op->Offset = offset & 0xFFFFFFFF;
372 op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
373 BOOL ok = ::WriteFile(impl.handle_,
374 boost::asio::buffer_cast<LPCVOID>(buffer),
375 static_cast<DWORD>(boost::asio::buffer_size(buffer)),
376 &bytes_transferred, op);
377 DWORD last_error = ::GetLastError();
378 if (!ok && last_error != ERROR_IO_PENDING
379 && last_error != ERROR_MORE_DATA)
381 iocp_service_.on_completion(op, last_error, bytes_transferred);
385 iocp_service_.on_pending(op);
390 size_t win_iocp_handle_service::do_read(
391 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
392 const boost::asio::mutable_buffer& buffer, boost::system::error_code& ec)
396 ec = boost::asio::error::bad_descriptor;
400 // A request to read 0 bytes on a stream handle is a no-op.
401 if (boost::asio::buffer_size(buffer) == 0)
403 ec = boost::system::error_code();
407 overlapped_wrapper overlapped(ec);
414 overlapped.Offset = offset & 0xFFFFFFFF;
415 overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
416 BOOL ok = ::ReadFile(impl.handle_,
417 boost::asio::buffer_cast<LPVOID>(buffer),
418 static_cast<DWORD>(boost::asio::buffer_size(buffer)), 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 (boost::asio::buffer_size(buffer) == 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_,
482 boost::asio::buffer_cast<LPVOID>(buffer),
483 static_cast<DWORD>(boost::asio::buffer_size(buffer)),
484 &bytes_transferred, op);
485 DWORD last_error = ::GetLastError();
486 if (!ok && last_error != ERROR_IO_PENDING
487 && last_error != ERROR_MORE_DATA)
489 iocp_service_.on_completion(op, last_error, bytes_transferred);
493 iocp_service_.on_pending(op);
498 void win_iocp_handle_service::update_cancellation_thread_id(
499 win_iocp_handle_service::implementation_type& impl)
501 if (impl.safe_cancellation_thread_id_ == 0)
502 impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
503 else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
504 impl.safe_cancellation_thread_id_ = ~DWORD(0);
507 void win_iocp_handle_service::close_for_destruction(implementation_type& impl)
511 BOOST_ASIO_HANDLER_OPERATION(("handle", &impl, "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