]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/asio/detail/impl/win_object_handle_service.ipp
5fbd49eb4f45829943ea0e43afdbfa594c72e25f
[ceph.git] / ceph / src / boost / boost / asio / detail / impl / win_object_handle_service.ipp
1 //
2 // detail/impl/win_object_handle_service.ipp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2023 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 BOOST_ASIO_ERROR_LOCATION(ec);
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 {
232 BOOST_ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
233 &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "close"));
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
268 scheduler_.post_deferred_completions(completed_ops);
269 }
270 else
271 {
272 ec = boost::system::error_code();
273 }
274
275 BOOST_ASIO_ERROR_LOCATION(ec);
276 return ec;
277 }
278
279 boost::system::error_code win_object_handle_service::cancel(
280 win_object_handle_service::implementation_type& impl,
281 boost::system::error_code& ec)
282 {
283 if (is_open(impl))
284 {
285 BOOST_ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle",
286 &impl, reinterpret_cast<uintmax_t>(impl.wait_handle_), "cancel"));
287
288 mutex::scoped_lock lock(mutex_);
289
290 HANDLE wait_handle = impl.wait_handle_;
291 impl.wait_handle_ = INVALID_HANDLE_VALUE;
292
293 op_queue<operation> completed_ops;
294 while (wait_op* op = impl.op_queue_.front())
295 {
296 op->ec_ = boost::asio::error::operation_aborted;
297 impl.op_queue_.pop();
298 completed_ops.push(op);
299 }
300
301 // We must not hold the lock while calling UnregisterWaitEx. This is
302 // because the registered callback function might be invoked while we are
303 // waiting for UnregisterWaitEx to complete.
304 lock.unlock();
305
306 if (wait_handle != INVALID_HANDLE_VALUE)
307 ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE);
308
309 ec = boost::system::error_code();
310
311 scheduler_.post_deferred_completions(completed_ops);
312 }
313 else
314 {
315 ec = boost::asio::error::bad_descriptor;
316 }
317
318 BOOST_ASIO_ERROR_LOCATION(ec);
319 return ec;
320 }
321
322 void win_object_handle_service::wait(
323 win_object_handle_service::implementation_type& impl,
324 boost::system::error_code& ec)
325 {
326 switch (::WaitForSingleObject(impl.handle_, INFINITE))
327 {
328 case WAIT_FAILED:
329 {
330 DWORD last_error = ::GetLastError();
331 ec = boost::system::error_code(last_error,
332 boost::asio::error::get_system_category());
333 BOOST_ASIO_ERROR_LOCATION(ec);
334 break;
335 }
336 case WAIT_OBJECT_0:
337 case WAIT_ABANDONED:
338 default:
339 ec = boost::system::error_code();
340 break;
341 }
342 }
343
344 void win_object_handle_service::start_wait_op(
345 win_object_handle_service::implementation_type& impl, wait_op* op)
346 {
347 scheduler_.work_started();
348
349 if (is_open(impl))
350 {
351 mutex::scoped_lock lock(mutex_);
352
353 if (!shutdown_)
354 {
355 impl.op_queue_.push(op);
356
357 // Only the first operation to be queued gets to register a wait callback.
358 // Subsequent operations have to wait for the first to finish.
359 if (impl.op_queue_.front() == op)
360 register_wait_callback(impl, lock);
361 }
362 else
363 {
364 lock.unlock();
365 scheduler_.post_deferred_completion(op);
366 }
367 }
368 else
369 {
370 op->ec_ = boost::asio::error::bad_descriptor;
371 scheduler_.post_deferred_completion(op);
372 }
373 }
374
375 void win_object_handle_service::register_wait_callback(
376 win_object_handle_service::implementation_type& impl,
377 mutex::scoped_lock& lock)
378 {
379 lock.lock();
380
381 if (!RegisterWaitForSingleObject(&impl.wait_handle_,
382 impl.handle_, &win_object_handle_service::wait_callback,
383 &impl, INFINITE, WT_EXECUTEONLYONCE))
384 {
385 DWORD last_error = ::GetLastError();
386 boost::system::error_code ec(last_error,
387 boost::asio::error::get_system_category());
388
389 op_queue<operation> completed_ops;
390 while (wait_op* op = impl.op_queue_.front())
391 {
392 op->ec_ = ec;
393 impl.op_queue_.pop();
394 completed_ops.push(op);
395 }
396
397 lock.unlock();
398 scheduler_.post_deferred_completions(completed_ops);
399 }
400 }
401
402 void win_object_handle_service::wait_callback(PVOID param, BOOLEAN)
403 {
404 implementation_type* impl = static_cast<implementation_type*>(param);
405 mutex::scoped_lock lock(impl->owner_->mutex_);
406
407 if (impl->wait_handle_ != INVALID_HANDLE_VALUE)
408 {
409 ::UnregisterWaitEx(impl->wait_handle_, NULL);
410 impl->wait_handle_ = INVALID_HANDLE_VALUE;
411 }
412
413 if (wait_op* op = impl->op_queue_.front())
414 {
415 op_queue<operation> completed_ops;
416
417 op->ec_ = boost::system::error_code();
418 impl->op_queue_.pop();
419 completed_ops.push(op);
420
421 if (!impl->op_queue_.empty())
422 {
423 if (!RegisterWaitForSingleObject(&impl->wait_handle_,
424 impl->handle_, &win_object_handle_service::wait_callback,
425 param, INFINITE, WT_EXECUTEONLYONCE))
426 {
427 DWORD last_error = ::GetLastError();
428 boost::system::error_code ec(last_error,
429 boost::asio::error::get_system_category());
430
431 while ((op = impl->op_queue_.front()) != 0)
432 {
433 op->ec_ = ec;
434 impl->op_queue_.pop();
435 completed_ops.push(op);
436 }
437 }
438 }
439
440 scheduler_impl& sched = impl->owner_->scheduler_;
441 lock.unlock();
442 sched.post_deferred_completions(completed_ops);
443 }
444 }
445
446 } // namespace detail
447 } // namespace asio
448 } // namespace boost
449
450 #include <boost/asio/detail/pop_options.hpp>
451
452 #endif // defined(BOOST_ASIO_HAS_WINDOWS_OBJECT_HANDLE)
453
454 #endif // BOOST_ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP