change support python version
[platform/upstream/boost.git] / boost / asio / detail / impl / win_object_handle_service.ipp
1 //
2 // detail/impl/win_object_handle_service.ipp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 // Copyright (c) 2011 Boris Schaeling (boris@highscore.de)
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
27 namespace boost {
28 namespace asio {
29 namespace detail {
30
31 win_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
40 void 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
58 void 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
77 void 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
112 void 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
164 void 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
210 boost::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
225 boost::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
277 boost::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
319 void 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
340 void 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
371 void 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
398 void 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