// | |
// detail/impl/win_iocp_handle_service.ipp | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
// | |
// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) | |
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) | |
// | |
// Distributed under the Boost Software License, Version 1.0. (See accompanying | |
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
// | |
#ifndef BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP | |
#define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP | |
#if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
# pragma once | |
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
#include <boost/asio/detail/config.hpp> | |
#if defined(BOOST_ASIO_HAS_IOCP) | |
#include <boost/asio/detail/win_iocp_handle_service.hpp> | |
#include <boost/asio/detail/push_options.hpp> | |
namespace boost { | |
namespace asio { | |
namespace detail { | |
class win_iocp_handle_service::overlapped_wrapper | |
: public OVERLAPPED | |
{ | |
public: | |
explicit overlapped_wrapper(boost::system::error_code& ec) | |
{ | |
Internal = 0; | |
InternalHigh = 0; | |
Offset = 0; | |
OffsetHigh = 0; | |
// Create a non-signalled manual-reset event, for GetOverlappedResult. | |
hEvent = ::CreateEvent(0, TRUE, FALSE, 0); | |
if (hEvent) | |
{ | |
// As documented in GetQueuedCompletionStatus, setting the low order | |
// bit of this event prevents our synchronous writes from being treated | |
// as completion port events. | |
*reinterpret_cast<DWORD_PTR*>(&hEvent) |= 1; | |
} | |
else | |
{ | |
DWORD last_error = ::GetLastError(); | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
} | |
} | |
~overlapped_wrapper() | |
{ | |
if (hEvent) | |
{ | |
::CloseHandle(hEvent); | |
} | |
} | |
}; | |
win_iocp_handle_service::win_iocp_handle_service( | |
boost::asio::io_service& io_service) | |
: iocp_service_(boost::asio::use_service<win_iocp_io_service>(io_service)), | |
mutex_(), | |
impl_list_(0) | |
{ | |
} | |
void win_iocp_handle_service::shutdown_service() | |
{ | |
// Close all implementations, causing all operations to complete. | |
boost::asio::detail::mutex::scoped_lock lock(mutex_); | |
implementation_type* impl = impl_list_; | |
while (impl) | |
{ | |
close_for_destruction(*impl); | |
impl = impl->next_; | |
} | |
} | |
void win_iocp_handle_service::construct( | |
win_iocp_handle_service::implementation_type& impl) | |
{ | |
impl.handle_ = INVALID_HANDLE_VALUE; | |
impl.safe_cancellation_thread_id_ = 0; | |
// Insert implementation into linked list of all implementations. | |
boost::asio::detail::mutex::scoped_lock lock(mutex_); | |
impl.next_ = impl_list_; | |
impl.prev_ = 0; | |
if (impl_list_) | |
impl_list_->prev_ = &impl; | |
impl_list_ = &impl; | |
} | |
void win_iocp_handle_service::destroy( | |
win_iocp_handle_service::implementation_type& impl) | |
{ | |
close_for_destruction(impl); | |
// Remove implementation from linked list of all implementations. | |
boost::asio::detail::mutex::scoped_lock lock(mutex_); | |
if (impl_list_ == &impl) | |
impl_list_ = impl.next_; | |
if (impl.prev_) | |
impl.prev_->next_ = impl.next_; | |
if (impl.next_) | |
impl.next_->prev_= impl.prev_; | |
impl.next_ = 0; | |
impl.prev_ = 0; | |
} | |
boost::system::error_code win_iocp_handle_service::assign( | |
win_iocp_handle_service::implementation_type& impl, | |
const native_type& native_handle, boost::system::error_code& ec) | |
{ | |
if (is_open(impl)) | |
{ | |
ec = boost::asio::error::already_open; | |
return ec; | |
} | |
if (iocp_service_.register_handle(native_handle, ec)) | |
return ec; | |
impl.handle_ = native_handle; | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
boost::system::error_code win_iocp_handle_service::close( | |
win_iocp_handle_service::implementation_type& impl, | |
boost::system::error_code& ec) | |
{ | |
if (is_open(impl)) | |
{ | |
if (!::CloseHandle(impl.handle_)) | |
{ | |
DWORD last_error = ::GetLastError(); | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
return ec; | |
} | |
impl.handle_ = INVALID_HANDLE_VALUE; | |
impl.safe_cancellation_thread_id_ = 0; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
boost::system::error_code win_iocp_handle_service::cancel( | |
win_iocp_handle_service::implementation_type& impl, | |
boost::system::error_code& ec) | |
{ | |
if (!is_open(impl)) | |
{ | |
ec = boost::asio::error::bad_descriptor; | |
} | |
else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( | |
::GetModuleHandleA("KERNEL32"), "CancelIoEx")) | |
{ | |
// The version of Windows supports cancellation from any thread. | |
typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); | |
cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; | |
if (!cancel_io_ex(impl.handle_, 0)) | |
{ | |
DWORD last_error = ::GetLastError(); | |
if (last_error == ERROR_NOT_FOUND) | |
{ | |
// ERROR_NOT_FOUND means that there were no operations to be | |
// cancelled. We swallow this error to match the behaviour on other | |
// platforms. | |
ec = boost::system::error_code(); | |
} | |
else | |
{ | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
} | |
} | |
else | |
{ | |
ec = boost::system::error_code(); | |
} | |
} | |
else if (impl.safe_cancellation_thread_id_ == 0) | |
{ | |
// No operations have been started, so there's nothing to cancel. | |
ec = boost::system::error_code(); | |
} | |
else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) | |
{ | |
// Asynchronous operations have been started from the current thread only, | |
// so it is safe to try to cancel them using CancelIo. | |
if (!::CancelIo(impl.handle_)) | |
{ | |
DWORD last_error = ::GetLastError(); | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
} | |
else | |
{ | |
ec = boost::system::error_code(); | |
} | |
} | |
else | |
{ | |
// Asynchronous operations have been started from more than one thread, | |
// so cancellation is not safe. | |
ec = boost::asio::error::operation_not_supported; | |
} | |
return ec; | |
} | |
size_t win_iocp_handle_service::do_write( | |
win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, | |
const boost::asio::const_buffer& buffer, boost::system::error_code& ec) | |
{ | |
if (!is_open(impl)) | |
{ | |
ec = boost::asio::error::bad_descriptor; | |
return 0; | |
} | |
// A request to write 0 bytes on a handle is a no-op. | |
if (boost::asio::buffer_size(buffer) == 0) | |
{ | |
ec = boost::system::error_code(); | |
return 0; | |
} | |
overlapped_wrapper overlapped(ec); | |
if (ec) | |
{ | |
return 0; | |
} | |
// Write the data. | |
overlapped.Offset = offset & 0xFFFFFFFF; | |
overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; | |
BOOL ok = ::WriteFile(impl.handle_, | |
boost::asio::buffer_cast<LPCVOID>(buffer), | |
static_cast<DWORD>(boost::asio::buffer_size(buffer)), 0, &overlapped); | |
if (!ok) | |
{ | |
DWORD last_error = ::GetLastError(); | |
if (last_error != ERROR_IO_PENDING) | |
{ | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
return 0; | |
} | |
} | |
// Wait for the operation to complete. | |
DWORD bytes_transferred = 0; | |
ok = ::GetOverlappedResult(impl.handle_, | |
&overlapped, &bytes_transferred, TRUE); | |
if (!ok) | |
{ | |
DWORD last_error = ::GetLastError(); | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
return 0; | |
} | |
ec = boost::system::error_code(); | |
return bytes_transferred; | |
} | |
void win_iocp_handle_service::start_write_op( | |
win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, | |
const boost::asio::const_buffer& buffer, operation* op) | |
{ | |
update_cancellation_thread_id(impl); | |
iocp_service_.work_started(); | |
if (!is_open(impl)) | |
{ | |
iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); | |
} | |
else if (boost::asio::buffer_size(buffer) == 0) | |
{ | |
// A request to write 0 bytes on a handle is a no-op. | |
iocp_service_.on_completion(op); | |
} | |
else | |
{ | |
DWORD bytes_transferred = 0; | |
op->Offset = offset & 0xFFFFFFFF; | |
op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; | |
BOOL ok = ::WriteFile(impl.handle_, | |
boost::asio::buffer_cast<LPCVOID>(buffer), | |
static_cast<DWORD>(boost::asio::buffer_size(buffer)), | |
&bytes_transferred, op); | |
DWORD last_error = ::GetLastError(); | |
if (!ok && last_error != ERROR_IO_PENDING | |
&& last_error != ERROR_MORE_DATA) | |
{ | |
iocp_service_.on_completion(op, last_error, bytes_transferred); | |
} | |
else | |
{ | |
iocp_service_.on_pending(op); | |
} | |
} | |
} | |
size_t win_iocp_handle_service::do_read( | |
win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, | |
const boost::asio::mutable_buffer& buffer, boost::system::error_code& ec) | |
{ | |
if (!is_open(impl)) | |
{ | |
ec = boost::asio::error::bad_descriptor; | |
return 0; | |
} | |
// A request to read 0 bytes on a stream handle is a no-op. | |
if (boost::asio::buffer_size(buffer) == 0) | |
{ | |
ec = boost::system::error_code(); | |
return 0; | |
} | |
overlapped_wrapper overlapped(ec); | |
if (ec) | |
{ | |
return 0; | |
} | |
// Read some data. | |
overlapped.Offset = offset & 0xFFFFFFFF; | |
overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; | |
BOOL ok = ::ReadFile(impl.handle_, | |
boost::asio::buffer_cast<LPVOID>(buffer), | |
static_cast<DWORD>(boost::asio::buffer_size(buffer)), 0, &overlapped); | |
if (!ok) | |
{ | |
DWORD last_error = ::GetLastError(); | |
if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) | |
{ | |
if (last_error == ERROR_HANDLE_EOF) | |
{ | |
ec = boost::asio::error::eof; | |
} | |
else | |
{ | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
} | |
return 0; | |
} | |
} | |
// Wait for the operation to complete. | |
DWORD bytes_transferred = 0; | |
ok = ::GetOverlappedResult(impl.handle_, | |
&overlapped, &bytes_transferred, TRUE); | |
if (!ok) | |
{ | |
DWORD last_error = ::GetLastError(); | |
if (last_error == ERROR_HANDLE_EOF) | |
{ | |
ec = boost::asio::error::eof; | |
} | |
else | |
{ | |
ec = boost::system::error_code(last_error, | |
boost::asio::error::get_system_category()); | |
} | |
return 0; | |
} | |
ec = boost::system::error_code(); | |
return bytes_transferred; | |
} | |
void win_iocp_handle_service::start_read_op( | |
win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, | |
const boost::asio::mutable_buffer& buffer, operation* op) | |
{ | |
update_cancellation_thread_id(impl); | |
iocp_service_.work_started(); | |
if (!is_open(impl)) | |
{ | |
iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); | |
} | |
else if (boost::asio::buffer_size(buffer) == 0) | |
{ | |
// A request to read 0 bytes on a handle is a no-op. | |
iocp_service_.on_completion(op); | |
} | |
else | |
{ | |
DWORD bytes_transferred = 0; | |
op->Offset = offset & 0xFFFFFFFF; | |
op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; | |
BOOL ok = ::ReadFile(impl.handle_, | |
boost::asio::buffer_cast<LPVOID>(buffer), | |
static_cast<DWORD>(boost::asio::buffer_size(buffer)), | |
&bytes_transferred, op); | |
DWORD last_error = ::GetLastError(); | |
if (!ok && last_error != ERROR_IO_PENDING | |
&& last_error != ERROR_MORE_DATA) | |
{ | |
iocp_service_.on_completion(op, last_error, bytes_transferred); | |
} | |
else | |
{ | |
iocp_service_.on_pending(op); | |
} | |
} | |
} | |
void win_iocp_handle_service::update_cancellation_thread_id( | |
win_iocp_handle_service::implementation_type& impl) | |
{ | |
if (impl.safe_cancellation_thread_id_ == 0) | |
impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); | |
else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) | |
impl.safe_cancellation_thread_id_ = ~DWORD(0); | |
} | |
void win_iocp_handle_service::close_for_destruction(implementation_type& impl) | |
{ | |
if (is_open(impl)) | |
{ | |
::CloseHandle(impl.handle_); | |
impl.handle_ = INVALID_HANDLE_VALUE; | |
impl.safe_cancellation_thread_id_ = 0; | |
} | |
} | |
} // namespace detail | |
} // namespace asio | |
} // namespace boost | |
#include <boost/asio/detail/pop_options.hpp> | |
#endif // defined(BOOST_ASIO_HAS_IOCP) | |
#endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP |