1// 2// detail/impl/win_iocp_handle_service.ipp 3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4// 5// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6// Copyright (c) 2008 Rep Invariant Systems, Inc. ([email protected]) 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 27namespace boost { 28namespace asio { 29namespace detail { 30 31class win_iocp_handle_service::overlapped_wrapper 32 : public OVERLAPPED 33{ 34public: 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 69win_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 77void 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 89void 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 104void 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 123void 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 162void 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 179boost::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 197boost::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 228boost::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 299size_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 354void 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 391size_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 460void 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 497void 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 506void 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 528