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