453 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			453 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // detail/impl/win_object_handle_service.ipp
 | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| //
 | |
| // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
 | |
| // Copyright (c) 2011 Boris Schaeling (boris@highscore.de)
 | |
| //
 | |
| // 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 ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP
 | |
| #define ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP
 | |
| 
 | |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| # pragma once
 | |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| 
 | |
| #include "asio/detail/config.hpp"
 | |
| 
 | |
| #if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE)
 | |
| 
 | |
| #include "asio/detail/win_object_handle_service.hpp"
 | |
| 
 | |
| #include "asio/detail/push_options.hpp"
 | |
| 
 | |
| namespace asio {
 | |
| namespace detail {
 | |
| 
 | |
| win_object_handle_service::win_object_handle_service(execution_context& context)
 | |
|   : execution_context_service_base<win_object_handle_service>(context),
 | |
|     scheduler_(asio::use_service<scheduler_impl>(context)),
 | |
|     mutex_(),
 | |
|     impl_list_(0),
 | |
|     shutdown_(false)
 | |
| {
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::shutdown()
 | |
| {
 | |
|   mutex::scoped_lock lock(mutex_);
 | |
| 
 | |
|   // Setting this flag to true prevents new objects from being registered, and
 | |
|   // new asynchronous wait operations from being started. We only need to worry
 | |
|   // about cleaning up the operations that are currently in progress.
 | |
|   shutdown_ = true;
 | |
| 
 | |
|   op_queue<operation> ops;
 | |
|   for (implementation_type* impl = impl_list_; impl; impl = impl->next_)
 | |
|     ops.push(impl->op_queue_);
 | |
| 
 | |
|   lock.unlock();
 | |
| 
 | |
|   scheduler_.abandon_operations(ops);
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::construct(
 | |
|     win_object_handle_service::implementation_type& impl)
 | |
| {
 | |
|   impl.handle_ = INVALID_HANDLE_VALUE;
 | |
|   impl.wait_handle_ = INVALID_HANDLE_VALUE;
 | |
|   impl.owner_ = this;
 | |
| 
 | |
|   // Insert implementation into linked list of all implementations.
 | |
|   mutex::scoped_lock lock(mutex_);
 | |
|   if (!shutdown_)
 | |
|   {
 | |
|     impl.next_ = impl_list_;
 | |
|     impl.prev_ = 0;
 | |
|     if (impl_list_)
 | |
|       impl_list_->prev_ = &impl;
 | |
|     impl_list_ = &impl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::move_construct(
 | |
|     win_object_handle_service::implementation_type& impl,
 | |
|     win_object_handle_service::implementation_type& other_impl)
 | |
| {
 | |
|   mutex::scoped_lock lock(mutex_);
 | |
| 
 | |
|   // Insert implementation into linked list of all implementations.
 | |
|   if (!shutdown_)
 | |
|   {
 | |
|     impl.next_ = impl_list_;
 | |
|     impl.prev_ = 0;
 | |
|     if (impl_list_)
 | |
|       impl_list_->prev_ = &impl;
 | |
|     impl_list_ = &impl;
 | |
|   }
 | |
| 
 | |
|   impl.handle_ = other_impl.handle_;
 | |
|   other_impl.handle_ = INVALID_HANDLE_VALUE;
 | |
|   impl.wait_handle_ = other_impl.wait_handle_;
 | |
|   other_impl.wait_handle_ = INVALID_HANDLE_VALUE;
 | |
|   impl.op_queue_.push(other_impl.op_queue_);
 | |
|   impl.owner_ = this;
 | |
| 
 | |
|   // We must not hold the lock while calling UnregisterWaitEx. This is because
 | |
|   // the registered callback function might be invoked while we are waiting for
 | |
|   // UnregisterWaitEx to complete.
 | |
|   lock.unlock();
 | |
| 
 | |
|   if (impl.wait_handle_ != INVALID_HANDLE_VALUE)
 | |
|     ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE);
 | |
| 
 | |
|   if (!impl.op_queue_.empty())
 | |
|     register_wait_callback(impl, lock);
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::move_assign(
 | |
|     win_object_handle_service::implementation_type& impl,
 | |
|     win_object_handle_service& other_service,
 | |
|     win_object_handle_service::implementation_type& other_impl)
 | |
| {
 | |
|   asio::error_code ignored_ec;
 | |
|   close(impl, ignored_ec);
 | |
| 
 | |
|   mutex::scoped_lock lock(mutex_);
 | |
| 
 | |
|   if (this != &other_service)
 | |
|   {
 | |
|     // Remove implementation from linked list of all implementations.
 | |
|     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;
 | |
|   }
 | |
| 
 | |
|   impl.handle_ = other_impl.handle_;
 | |
|   other_impl.handle_ = INVALID_HANDLE_VALUE;
 | |
|   impl.wait_handle_ = other_impl.wait_handle_;
 | |
|   other_impl.wait_handle_ = INVALID_HANDLE_VALUE;
 | |
|   impl.op_queue_.push(other_impl.op_queue_);
 | |
|   impl.owner_ = this;
 | |
| 
 | |
|   if (this != &other_service)
 | |
|   {
 | |
|     // Insert implementation into linked list of all implementations.
 | |
|     impl.next_ = other_service.impl_list_;
 | |
|     impl.prev_ = 0;
 | |
|     if (other_service.impl_list_)
 | |
|       other_service.impl_list_->prev_ = &impl;
 | |
|     other_service.impl_list_ = &impl;
 | |
|   }
 | |
| 
 | |
|   // We must not hold the lock while calling UnregisterWaitEx. This is because
 | |
|   // the registered callback function might be invoked while we are waiting for
 | |
|   // UnregisterWaitEx to complete.
 | |
|   lock.unlock();
 | |
| 
 | |
|   if (impl.wait_handle_ != INVALID_HANDLE_VALUE)
 | |
|     ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE);
 | |
| 
 | |
|   if (!impl.op_queue_.empty())
 | |
|     register_wait_callback(impl, lock);
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::destroy(
 | |
|     win_object_handle_service::implementation_type& impl)
 | |
| {
 | |
|   mutex::scoped_lock lock(mutex_);
 | |
| 
 | |
|   // Remove implementation from linked list of all implementations.
 | |
|   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;
 | |
| 
 | |
|   if (is_open(impl))
 | |
|   {
 | |
|     ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
 | |
|           &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "close"));
 | |
| 
 | |
|     HANDLE wait_handle = impl.wait_handle_;
 | |
|     impl.wait_handle_ = INVALID_HANDLE_VALUE;
 | |
| 
 | |
|     op_queue<operation> ops;
 | |
|     while (wait_op* op = impl.op_queue_.front())
 | |
|     {
 | |
|       op->ec_ = asio::error::operation_aborted;
 | |
|       impl.op_queue_.pop();
 | |
|       ops.push(op);
 | |
|     }
 | |
| 
 | |
|     // We must not hold the lock while calling UnregisterWaitEx. This is
 | |
|     // because the registered callback function might be invoked while we are
 | |
|     // waiting for UnregisterWaitEx to complete.
 | |
|     lock.unlock();
 | |
| 
 | |
|     if (wait_handle != INVALID_HANDLE_VALUE)
 | |
|       ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE);
 | |
| 
 | |
|     ::CloseHandle(impl.handle_);
 | |
|     impl.handle_ = INVALID_HANDLE_VALUE;
 | |
| 
 | |
|     scheduler_.post_deferred_completions(ops);
 | |
|   }
 | |
| }
 | |
| 
 | |
| asio::error_code win_object_handle_service::assign(
 | |
|     win_object_handle_service::implementation_type& impl,
 | |
|     const native_handle_type& handle, asio::error_code& ec)
 | |
| {
 | |
|   if (is_open(impl))
 | |
|   {
 | |
|     ec = asio::error::already_open;
 | |
|     ASIO_ERROR_LOCATION(ec);
 | |
|     return ec;
 | |
|   }
 | |
| 
 | |
|   impl.handle_ = handle;
 | |
|   ec = asio::error_code();
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| asio::error_code win_object_handle_service::close(
 | |
|     win_object_handle_service::implementation_type& impl,
 | |
|     asio::error_code& ec)
 | |
| {
 | |
|   if (is_open(impl))
 | |
|   {
 | |
|     ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
 | |
|           &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "close"));
 | |
| 
 | |
|     mutex::scoped_lock lock(mutex_);
 | |
| 
 | |
|     HANDLE wait_handle = impl.wait_handle_;
 | |
|     impl.wait_handle_ = INVALID_HANDLE_VALUE;
 | |
| 
 | |
|     op_queue<operation> completed_ops;
 | |
|     while (wait_op* op = impl.op_queue_.front())
 | |
|     {
 | |
|       impl.op_queue_.pop();
 | |
|       op->ec_ = asio::error::operation_aborted;
 | |
|       completed_ops.push(op);
 | |
|     }
 | |
| 
 | |
|     // We must not hold the lock while calling UnregisterWaitEx. This is
 | |
|     // because the registered callback function might be invoked while we are
 | |
|     // waiting for UnregisterWaitEx to complete.
 | |
|     lock.unlock();
 | |
| 
 | |
|     if (wait_handle != INVALID_HANDLE_VALUE)
 | |
|       ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE);
 | |
| 
 | |
|     if (::CloseHandle(impl.handle_))
 | |
|     {
 | |
|       impl.handle_ = INVALID_HANDLE_VALUE;
 | |
|       ec = asio::error_code();
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       DWORD last_error = ::GetLastError();
 | |
|       ec = asio::error_code(last_error,
 | |
|           asio::error::get_system_category());
 | |
|     }
 | |
| 
 | |
|     scheduler_.post_deferred_completions(completed_ops);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     ec = asio::error_code();
 | |
|   }
 | |
| 
 | |
|   ASIO_ERROR_LOCATION(ec);
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| asio::error_code win_object_handle_service::cancel(
 | |
|     win_object_handle_service::implementation_type& impl,
 | |
|     asio::error_code& ec)
 | |
| {
 | |
|   if (is_open(impl))
 | |
|   {
 | |
|     ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
 | |
|           &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "cancel"));
 | |
| 
 | |
|     mutex::scoped_lock lock(mutex_);
 | |
| 
 | |
|     HANDLE wait_handle = impl.wait_handle_;
 | |
|     impl.wait_handle_ = INVALID_HANDLE_VALUE;
 | |
| 
 | |
|     op_queue<operation> completed_ops;
 | |
|     while (wait_op* op = impl.op_queue_.front())
 | |
|     {
 | |
|       op->ec_ = asio::error::operation_aborted;
 | |
|       impl.op_queue_.pop();
 | |
|       completed_ops.push(op);
 | |
|     }
 | |
| 
 | |
|     // We must not hold the lock while calling UnregisterWaitEx. This is
 | |
|     // because the registered callback function might be invoked while we are
 | |
|     // waiting for UnregisterWaitEx to complete.
 | |
|     lock.unlock();
 | |
| 
 | |
|     if (wait_handle != INVALID_HANDLE_VALUE)
 | |
|       ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE);
 | |
| 
 | |
|     ec = asio::error_code();
 | |
| 
 | |
|     scheduler_.post_deferred_completions(completed_ops);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     ec = asio::error::bad_descriptor;
 | |
|   }
 | |
| 
 | |
|   ASIO_ERROR_LOCATION(ec);
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::wait(
 | |
|     win_object_handle_service::implementation_type& impl,
 | |
|     asio::error_code& ec)
 | |
| {
 | |
|   switch (::WaitForSingleObject(impl.handle_, INFINITE))
 | |
|   {
 | |
|   case WAIT_FAILED:
 | |
|     {
 | |
|       DWORD last_error = ::GetLastError();
 | |
|       ec = asio::error_code(last_error,
 | |
|           asio::error::get_system_category());
 | |
|       ASIO_ERROR_LOCATION(ec);
 | |
|       break;
 | |
|     }
 | |
|   case WAIT_OBJECT_0:
 | |
|   case WAIT_ABANDONED:
 | |
|   default:
 | |
|     ec = asio::error_code();
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::start_wait_op(
 | |
|     win_object_handle_service::implementation_type& impl, wait_op* op)
 | |
| {
 | |
|   scheduler_.work_started();
 | |
| 
 | |
|   if (is_open(impl))
 | |
|   {
 | |
|     mutex::scoped_lock lock(mutex_);
 | |
| 
 | |
|     if (!shutdown_)
 | |
|     {
 | |
|       impl.op_queue_.push(op);
 | |
| 
 | |
|       // Only the first operation to be queued gets to register a wait callback.
 | |
|       // Subsequent operations have to wait for the first to finish.
 | |
|       if (impl.op_queue_.front() == op)
 | |
|         register_wait_callback(impl, lock);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       lock.unlock();
 | |
|       scheduler_.post_deferred_completion(op);
 | |
|     }
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     op->ec_ = asio::error::bad_descriptor;
 | |
|     scheduler_.post_deferred_completion(op);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::register_wait_callback(
 | |
|     win_object_handle_service::implementation_type& impl,
 | |
|     mutex::scoped_lock& lock)
 | |
| {
 | |
|   lock.lock();
 | |
| 
 | |
|   if (!RegisterWaitForSingleObject(&impl.wait_handle_,
 | |
|         impl.handle_, &win_object_handle_service::wait_callback,
 | |
|         &impl, INFINITE, WT_EXECUTEONLYONCE))
 | |
|   {
 | |
|     DWORD last_error = ::GetLastError();
 | |
|     asio::error_code ec(last_error,
 | |
|         asio::error::get_system_category());
 | |
| 
 | |
|     op_queue<operation> completed_ops;
 | |
|     while (wait_op* op = impl.op_queue_.front())
 | |
|     {
 | |
|       op->ec_ = ec;
 | |
|       impl.op_queue_.pop();
 | |
|       completed_ops.push(op);
 | |
|     }
 | |
| 
 | |
|     lock.unlock();
 | |
|     scheduler_.post_deferred_completions(completed_ops);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void win_object_handle_service::wait_callback(PVOID param, BOOLEAN)
 | |
| {
 | |
|   implementation_type* impl = static_cast<implementation_type*>(param);
 | |
|   mutex::scoped_lock lock(impl->owner_->mutex_);
 | |
| 
 | |
|   if (impl->wait_handle_ != INVALID_HANDLE_VALUE)
 | |
|   {
 | |
|     ::UnregisterWaitEx(impl->wait_handle_, NULL);
 | |
|     impl->wait_handle_ = INVALID_HANDLE_VALUE;
 | |
|   }
 | |
| 
 | |
|   if (wait_op* op = impl->op_queue_.front())
 | |
|   {
 | |
|     op_queue<operation> completed_ops;
 | |
| 
 | |
|     op->ec_ = asio::error_code();
 | |
|     impl->op_queue_.pop();
 | |
|     completed_ops.push(op);
 | |
| 
 | |
|     if (!impl->op_queue_.empty())
 | |
|     {
 | |
|       if (!RegisterWaitForSingleObject(&impl->wait_handle_,
 | |
|             impl->handle_, &win_object_handle_service::wait_callback,
 | |
|             param, INFINITE, WT_EXECUTEONLYONCE))
 | |
|       {
 | |
|         DWORD last_error = ::GetLastError();
 | |
|         asio::error_code ec(last_error,
 | |
|             asio::error::get_system_category());
 | |
| 
 | |
|         while ((op = impl->op_queue_.front()) != 0)
 | |
|         {
 | |
|           op->ec_ = ec;
 | |
|           impl->op_queue_.pop();
 | |
|           completed_ops.push(op);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     scheduler_impl& sched = impl->owner_->scheduler_;
 | |
|     lock.unlock();
 | |
|     sched.post_deferred_completions(completed_ops);
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // namespace detail
 | |
| } // namespace asio
 | |
| 
 | |
| #include "asio/detail/pop_options.hpp"
 | |
| 
 | |
| #endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE)
 | |
| 
 | |
| #endif // ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP
 |