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