1// 2// detail/impl/win_object_handle_service.ipp 3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4// 5// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6// Copyright (c) 2011 Boris Schaeling ([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_OBJECT_HANDLE_SERVICE_IPP 13#define BOOST_ASIO_DETAIL_IMPL_WIN_OBJECT_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_WINDOWS_OBJECT_HANDLE) 22 23#include <boost/asio/detail/win_object_handle_service.hpp> 24 25#include <boost/asio/detail/push_options.hpp> 26 27namespace boost { 28namespace asio { 29namespace detail { 30 31win_object_handle_service::win_object_handle_service(execution_context& context) 32 : execution_context_service_base<win_object_handle_service>(context), 33 scheduler_(boost::asio::use_service<scheduler_impl>(context)), 34 mutex_(), 35 impl_list_(0), 36 shutdown_(false) 37{ 38} 39 40void win_object_handle_service::shutdown() 41{ 42 mutex::scoped_lock lock(mutex_); 43 44 // Setting this flag to true prevents new objects from being registered, and 45 // new asynchronous wait operations from being started. We only need to worry 46 // about cleaning up the operations that are currently in progress. 47 shutdown_ = true; 48 49 op_queue<operation> ops; 50 for (implementation_type* impl = impl_list_; impl; impl = impl->next_) 51 ops.push(impl->op_queue_); 52 53 lock.unlock(); 54 55 scheduler_.abandon_operations(ops); 56} 57 58void win_object_handle_service::construct( 59 win_object_handle_service::implementation_type& impl) 60{ 61 impl.handle_ = INVALID_HANDLE_VALUE; 62 impl.wait_handle_ = INVALID_HANDLE_VALUE; 63 impl.owner_ = this; 64 65 // Insert implementation into linked list of all implementations. 66 mutex::scoped_lock lock(mutex_); 67 if (!shutdown_) 68 { 69 impl.next_ = impl_list_; 70 impl.prev_ = 0; 71 if (impl_list_) 72 impl_list_->prev_ = &impl; 73 impl_list_ = &impl; 74 } 75} 76 77void win_object_handle_service::move_construct( 78 win_object_handle_service::implementation_type& impl, 79 win_object_handle_service::implementation_type& other_impl) 80{ 81 mutex::scoped_lock lock(mutex_); 82 83 // Insert implementation into linked list of all implementations. 84 if (!shutdown_) 85 { 86 impl.next_ = impl_list_; 87 impl.prev_ = 0; 88 if (impl_list_) 89 impl_list_->prev_ = &impl; 90 impl_list_ = &impl; 91 } 92 93 impl.handle_ = other_impl.handle_; 94 other_impl.handle_ = INVALID_HANDLE_VALUE; 95 impl.wait_handle_ = other_impl.wait_handle_; 96 other_impl.wait_handle_ = INVALID_HANDLE_VALUE; 97 impl.op_queue_.push(other_impl.op_queue_); 98 impl.owner_ = this; 99 100 // We must not hold the lock while calling UnregisterWaitEx. This is because 101 // the registered callback function might be invoked while we are waiting for 102 // UnregisterWaitEx to complete. 103 lock.unlock(); 104 105 if (impl.wait_handle_ != INVALID_HANDLE_VALUE) 106 ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE); 107 108 if (!impl.op_queue_.empty()) 109 register_wait_callback(impl, lock); 110} 111 112void win_object_handle_service::move_assign( 113 win_object_handle_service::implementation_type& impl, 114 win_object_handle_service& other_service, 115 win_object_handle_service::implementation_type& other_impl) 116{ 117 boost::system::error_code ignored_ec; 118 close(impl, ignored_ec); 119 120 mutex::scoped_lock lock(mutex_); 121 122 if (this != &other_service) 123 { 124 // Remove implementation from linked list of all implementations. 125 if (impl_list_ == &impl) 126 impl_list_ = impl.next_; 127 if (impl.prev_) 128 impl.prev_->next_ = impl.next_; 129 if (impl.next_) 130 impl.next_->prev_= impl.prev_; 131 impl.next_ = 0; 132 impl.prev_ = 0; 133 } 134 135 impl.handle_ = other_impl.handle_; 136 other_impl.handle_ = INVALID_HANDLE_VALUE; 137 impl.wait_handle_ = other_impl.wait_handle_; 138 other_impl.wait_handle_ = INVALID_HANDLE_VALUE; 139 impl.op_queue_.push(other_impl.op_queue_); 140 impl.owner_ = this; 141 142 if (this != &other_service) 143 { 144 // Insert implementation into linked list of all implementations. 145 impl.next_ = other_service.impl_list_; 146 impl.prev_ = 0; 147 if (other_service.impl_list_) 148 other_service.impl_list_->prev_ = &impl; 149 other_service.impl_list_ = &impl; 150 } 151 152 // We must not hold the lock while calling UnregisterWaitEx. This is because 153 // the registered callback function might be invoked while we are waiting for 154 // UnregisterWaitEx to complete. 155 lock.unlock(); 156 157 if (impl.wait_handle_ != INVALID_HANDLE_VALUE) 158 ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE); 159 160 if (!impl.op_queue_.empty()) 161 register_wait_callback(impl, lock); 162} 163 164void win_object_handle_service::destroy( 165 win_object_handle_service::implementation_type& impl) 166{ 167 mutex::scoped_lock lock(mutex_); 168 169 // Remove implementation from linked list of all implementations. 170 if (impl_list_ == &impl) 171 impl_list_ = impl.next_; 172 if (impl.prev_) 173 impl.prev_->next_ = impl.next_; 174 if (impl.next_) 175 impl.next_->prev_= impl.prev_; 176 impl.next_ = 0; 177 impl.prev_ = 0; 178 179 if (is_open(impl)) 180 { 181 BOOST_ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle", 182 &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "close")); 183 184 HANDLE wait_handle = impl.wait_handle_; 185 impl.wait_handle_ = INVALID_HANDLE_VALUE; 186 187 op_queue<operation> ops; 188 while (wait_op* op = impl.op_queue_.front()) 189 { 190 op->ec_ = boost::asio::error::operation_aborted; 191 impl.op_queue_.pop(); 192 ops.push(op); 193 } 194 195 // We must not hold the lock while calling UnregisterWaitEx. This is 196 // because the registered callback function might be invoked while we are 197 // waiting for UnregisterWaitEx to complete. 198 lock.unlock(); 199 200 if (wait_handle != INVALID_HANDLE_VALUE) 201 ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); 202 203 ::CloseHandle(impl.handle_); 204 impl.handle_ = INVALID_HANDLE_VALUE; 205 206 scheduler_.post_deferred_completions(ops); 207 } 208} 209 210boost::system::error_code win_object_handle_service::assign( 211 win_object_handle_service::implementation_type& impl, 212 const native_handle_type& handle, boost::system::error_code& ec) 213{ 214 if (is_open(impl)) 215 { 216 ec = boost::asio::error::already_open; 217 return ec; 218 } 219 220 impl.handle_ = handle; 221 ec = boost::system::error_code(); 222 return ec; 223} 224 225boost::system::error_code win_object_handle_service::close( 226 win_object_handle_service::implementation_type& impl, 227 boost::system::error_code& ec) 228{ 229 if (is_open(impl)) 230 { 231 BOOST_ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle", 232 &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "close")); 233 234 mutex::scoped_lock lock(mutex_); 235 236 HANDLE wait_handle = impl.wait_handle_; 237 impl.wait_handle_ = INVALID_HANDLE_VALUE; 238 239 op_queue<operation> completed_ops; 240 while (wait_op* op = impl.op_queue_.front()) 241 { 242 impl.op_queue_.pop(); 243 op->ec_ = boost::asio::error::operation_aborted; 244 completed_ops.push(op); 245 } 246 247 // We must not hold the lock while calling UnregisterWaitEx. This is 248 // because the registered callback function might be invoked while we are 249 // waiting for UnregisterWaitEx to complete. 250 lock.unlock(); 251 252 if (wait_handle != INVALID_HANDLE_VALUE) 253 ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); 254 255 if (::CloseHandle(impl.handle_)) 256 { 257 impl.handle_ = INVALID_HANDLE_VALUE; 258 ec = boost::system::error_code(); 259 } 260 else 261 { 262 DWORD last_error = ::GetLastError(); 263 ec = boost::system::error_code(last_error, 264 boost::asio::error::get_system_category()); 265 } 266 267 scheduler_.post_deferred_completions(completed_ops); 268 } 269 else 270 { 271 ec = boost::system::error_code(); 272 } 273 274 return ec; 275} 276 277boost::system::error_code win_object_handle_service::cancel( 278 win_object_handle_service::implementation_type& impl, 279 boost::system::error_code& ec) 280{ 281 if (is_open(impl)) 282 { 283 BOOST_ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle", 284 &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "cancel")); 285 286 mutex::scoped_lock lock(mutex_); 287 288 HANDLE wait_handle = impl.wait_handle_; 289 impl.wait_handle_ = INVALID_HANDLE_VALUE; 290 291 op_queue<operation> completed_ops; 292 while (wait_op* op = impl.op_queue_.front()) 293 { 294 op->ec_ = boost::asio::error::operation_aborted; 295 impl.op_queue_.pop(); 296 completed_ops.push(op); 297 } 298 299 // We must not hold the lock while calling UnregisterWaitEx. This is 300 // because the registered callback function might be invoked while we are 301 // waiting for UnregisterWaitEx to complete. 302 lock.unlock(); 303 304 if (wait_handle != INVALID_HANDLE_VALUE) 305 ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); 306 307 ec = boost::system::error_code(); 308 309 scheduler_.post_deferred_completions(completed_ops); 310 } 311 else 312 { 313 ec = boost::asio::error::bad_descriptor; 314 } 315 316 return ec; 317} 318 319void win_object_handle_service::wait( 320 win_object_handle_service::implementation_type& impl, 321 boost::system::error_code& ec) 322{ 323 switch (::WaitForSingleObject(impl.handle_, INFINITE)) 324 { 325 case WAIT_FAILED: 326 { 327 DWORD last_error = ::GetLastError(); 328 ec = boost::system::error_code(last_error, 329 boost::asio::error::get_system_category()); 330 break; 331 } 332 case WAIT_OBJECT_0: 333 case WAIT_ABANDONED: 334 default: 335 ec = boost::system::error_code(); 336 break; 337 } 338} 339 340void win_object_handle_service::start_wait_op( 341 win_object_handle_service::implementation_type& impl, wait_op* op) 342{ 343 scheduler_.work_started(); 344 345 if (is_open(impl)) 346 { 347 mutex::scoped_lock lock(mutex_); 348 349 if (!shutdown_) 350 { 351 impl.op_queue_.push(op); 352 353 // Only the first operation to be queued gets to register a wait callback. 354 // Subsequent operations have to wait for the first to finish. 355 if (impl.op_queue_.front() == op) 356 register_wait_callback(impl, lock); 357 } 358 else 359 { 360 lock.unlock(); 361 scheduler_.post_deferred_completion(op); 362 } 363 } 364 else 365 { 366 op->ec_ = boost::asio::error::bad_descriptor; 367 scheduler_.post_deferred_completion(op); 368 } 369} 370 371void win_object_handle_service::register_wait_callback( 372 win_object_handle_service::implementation_type& impl, 373 mutex::scoped_lock& lock) 374{ 375 lock.lock(); 376 377 if (!RegisterWaitForSingleObject(&impl.wait_handle_, 378 impl.handle_, &win_object_handle_service::wait_callback, 379 &impl, INFINITE, WT_EXECUTEONLYONCE)) 380 { 381 DWORD last_error = ::GetLastError(); 382 boost::system::error_code ec(last_error, 383 boost::asio::error::get_system_category()); 384 385 op_queue<operation> completed_ops; 386 while (wait_op* op = impl.op_queue_.front()) 387 { 388 op->ec_ = ec; 389 impl.op_queue_.pop(); 390 completed_ops.push(op); 391 } 392 393 lock.unlock(); 394 scheduler_.post_deferred_completions(completed_ops); 395 } 396} 397 398void win_object_handle_service::wait_callback(PVOID param, BOOLEAN) 399{ 400 implementation_type* impl = static_cast<implementation_type*>(param); 401 mutex::scoped_lock lock(impl->owner_->mutex_); 402 403 if (impl->wait_handle_ != INVALID_HANDLE_VALUE) 404 { 405 ::UnregisterWaitEx(impl->wait_handle_, NULL); 406 impl->wait_handle_ = INVALID_HANDLE_VALUE; 407 } 408 409 if (wait_op* op = impl->op_queue_.front()) 410 { 411 op_queue<operation> completed_ops; 412 413 op->ec_ = boost::system::error_code(); 414 impl->op_queue_.pop(); 415 completed_ops.push(op); 416 417 if (!impl->op_queue_.empty()) 418 { 419 if (!RegisterWaitForSingleObject(&impl->wait_handle_, 420 impl->handle_, &win_object_handle_service::wait_callback, 421 param, INFINITE, WT_EXECUTEONLYONCE)) 422 { 423 DWORD last_error = ::GetLastError(); 424 boost::system::error_code ec(last_error, 425 boost::asio::error::get_system_category()); 426 427 while ((op = impl->op_queue_.front()) != 0) 428 { 429 op->ec_ = ec; 430 impl->op_queue_.pop(); 431 completed_ops.push(op); 432 } 433 } 434 } 435 436 scheduler_impl& sched = impl->owner_->scheduler_; 437 lock.unlock(); 438 sched.post_deferred_completions(completed_ops); 439 } 440} 441 442} // namespace detail 443} // namespace asio 444} // namespace boost 445 446#include <boost/asio/detail/pop_options.hpp> 447 448#endif // defined(BOOST_ASIO_HAS_WINDOWS_OBJECT_HANDLE) 449 450#endif // BOOST_ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP 451