]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/asio/include/boost/asio/detail/impl/win_iocp_handle_service.ipp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / asio / include / boost / asio / detail / impl / win_iocp_handle_service.ipp
1 //
2 // detail/impl/win_iocp_handle_service.ipp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com)
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_IOCP_HANDLE_SERVICE_IPP
13 #define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_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_IOCP)
22
23 #include <boost/asio/detail/win_iocp_handle_service.hpp>
24
25 #include <boost/asio/detail/push_options.hpp>
26
27 namespace boost {
28 namespace asio {
29 namespace detail {
30
31 class win_iocp_handle_service::overlapped_wrapper
32 : public OVERLAPPED
33 {
34 public:
35 explicit overlapped_wrapper(boost::system::error_code& ec)
36 {
37 Internal = 0;
38 InternalHigh = 0;
39 Offset = 0;
40 OffsetHigh = 0;
41
42 // Create a non-signalled manual-reset event, for GetOverlappedResult.
43 hEvent = ::CreateEventW(0, TRUE, FALSE, 0);
44 if (hEvent)
45 {
46 // As documented in GetQueuedCompletionStatus, setting the low order
47 // bit of this event prevents our synchronous writes from being treated
48 // as completion port events.
49 DWORD_PTR tmp = reinterpret_cast<DWORD_PTR>(hEvent);
50 hEvent = reinterpret_cast<HANDLE>(tmp | 1);
51 }
52 else
53 {
54 DWORD last_error = ::GetLastError();
55 ec = boost::system::error_code(last_error,
56 boost::asio::error::get_system_category());
57 }
58 }
59
60 ~overlapped_wrapper()
61 {
62 if (hEvent)
63 {
64 ::CloseHandle(hEvent);
65 }
66 }
67 };
68
69 win_iocp_handle_service::win_iocp_handle_service(
70 boost::asio::io_service& io_service)
71 : iocp_service_(boost::asio::use_service<win_iocp_io_service>(io_service)),
72 mutex_(),
73 impl_list_(0)
74 {
75 }
76
77 void win_iocp_handle_service::shutdown_service()
78 {
79 // Close all implementations, causing all operations to complete.
80 boost::asio::detail::mutex::scoped_lock lock(mutex_);
81 implementation_type* impl = impl_list_;
82 while (impl)
83 {
84 close_for_destruction(*impl);
85 impl = impl->next_;
86 }
87 }
88
89 void win_iocp_handle_service::construct(
90 win_iocp_handle_service::implementation_type& impl)
91 {
92 impl.handle_ = INVALID_HANDLE_VALUE;
93 impl.safe_cancellation_thread_id_ = 0;
94
95 // Insert implementation into linked list of all implementations.
96 boost::asio::detail::mutex::scoped_lock lock(mutex_);
97 impl.next_ = impl_list_;
98 impl.prev_ = 0;
99 if (impl_list_)
100 impl_list_->prev_ = &impl;
101 impl_list_ = &impl;
102 }
103
104 void win_iocp_handle_service::move_construct(
105 win_iocp_handle_service::implementation_type& impl,
106 win_iocp_handle_service::implementation_type& other_impl)
107 {
108 impl.handle_ = other_impl.handle_;
109 other_impl.handle_ = INVALID_HANDLE_VALUE;
110
111 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
112 other_impl.safe_cancellation_thread_id_ = 0;
113
114 // Insert implementation into linked list of all implementations.
115 boost::asio::detail::mutex::scoped_lock lock(mutex_);
116 impl.next_ = impl_list_;
117 impl.prev_ = 0;
118 if (impl_list_)
119 impl_list_->prev_ = &impl;
120 impl_list_ = &impl;
121 }
122
123 void win_iocp_handle_service::move_assign(
124 win_iocp_handle_service::implementation_type& impl,
125 win_iocp_handle_service& other_service,
126 win_iocp_handle_service::implementation_type& other_impl)
127 {
128 close_for_destruction(impl);
129
130 if (this != &other_service)
131 {
132 // Remove implementation from linked list of all implementations.
133 boost::asio::detail::mutex::scoped_lock lock(mutex_);
134 if (impl_list_ == &impl)
135 impl_list_ = impl.next_;
136 if (impl.prev_)
137 impl.prev_->next_ = impl.next_;
138 if (impl.next_)
139 impl.next_->prev_= impl.prev_;
140 impl.next_ = 0;
141 impl.prev_ = 0;
142 }
143
144 impl.handle_ = other_impl.handle_;
145 other_impl.handle_ = INVALID_HANDLE_VALUE;
146
147 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
148 other_impl.safe_cancellation_thread_id_ = 0;
149
150 if (this != &other_service)
151 {
152 // Insert implementation into linked list of all implementations.
153 boost::asio::detail::mutex::scoped_lock lock(other_service.mutex_);
154 impl.next_ = other_service.impl_list_;
155 impl.prev_ = 0;
156 if (other_service.impl_list_)
157 other_service.impl_list_->prev_ = &impl;
158 other_service.impl_list_ = &impl;
159 }
160 }
161
162 void win_iocp_handle_service::destroy(
163 win_iocp_handle_service::implementation_type& impl)
164 {
165 close_for_destruction(impl);
166
167 // Remove implementation from linked list of all implementations.
168 boost::asio::detail::mutex::scoped_lock lock(mutex_);
169 if (impl_list_ == &impl)
170 impl_list_ = impl.next_;
171 if (impl.prev_)
172 impl.prev_->next_ = impl.next_;
173 if (impl.next_)
174 impl.next_->prev_= impl.prev_;
175 impl.next_ = 0;
176 impl.prev_ = 0;
177 }
178
179 boost::system::error_code win_iocp_handle_service::assign(
180 win_iocp_handle_service::implementation_type& impl,
181 const native_handle_type& handle, boost::system::error_code& ec)
182 {
183 if (is_open(impl))
184 {
185 ec = boost::asio::error::already_open;
186 return ec;
187 }
188
189 if (iocp_service_.register_handle(handle, ec))
190 return ec;
191
192 impl.handle_ = handle;
193 ec = boost::system::error_code();
194 return ec;
195 }
196
197 boost::system::error_code win_iocp_handle_service::close(
198 win_iocp_handle_service::implementation_type& impl,
199 boost::system::error_code& ec)
200 {
201 if (is_open(impl))
202 {
203 BOOST_ASIO_HANDLER_OPERATION(("handle", &impl, "close"));
204
205 if (!::CloseHandle(impl.handle_))
206 {
207 DWORD last_error = ::GetLastError();
208 ec = boost::system::error_code(last_error,
209 boost::asio::error::get_system_category());
210 }
211 else
212 {
213 ec = boost::system::error_code();
214 }
215
216 impl.handle_ = INVALID_HANDLE_VALUE;
217 impl.safe_cancellation_thread_id_ = 0;
218 }
219 else
220 {
221 ec = boost::system::error_code();
222 }
223
224 return ec;
225 }
226
227 boost::system::error_code win_iocp_handle_service::cancel(
228 win_iocp_handle_service::implementation_type& impl,
229 boost::system::error_code& ec)
230 {
231 if (!is_open(impl))
232 {
233 ec = boost::asio::error::bad_descriptor;
234 return ec;
235 }
236
237 BOOST_ASIO_HANDLER_OPERATION(("handle", &impl, "cancel"));
238
239 if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
240 ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
241 {
242 // The version of Windows supports cancellation from any thread.
243 typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
244 cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
245 if (!cancel_io_ex(impl.handle_, 0))
246 {
247 DWORD last_error = ::GetLastError();
248 if (last_error == ERROR_NOT_FOUND)
249 {
250 // ERROR_NOT_FOUND means that there were no operations to be
251 // cancelled. We swallow this error to match the behaviour on other
252 // platforms.
253 ec = boost::system::error_code();
254 }
255 else
256 {
257 ec = boost::system::error_code(last_error,
258 boost::asio::error::get_system_category());
259 }
260 }
261 else
262 {
263 ec = boost::system::error_code();
264 }
265 }
266 else if (impl.safe_cancellation_thread_id_ == 0)
267 {
268 // No operations have been started, so there's nothing to cancel.
269 ec = boost::system::error_code();
270 }
271 else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
272 {
273 // Asynchronous operations have been started from the current thread only,
274 // so it is safe to try to cancel them using CancelIo.
275 if (!::CancelIo(impl.handle_))
276 {
277 DWORD last_error = ::GetLastError();
278 ec = boost::system::error_code(last_error,
279 boost::asio::error::get_system_category());
280 }
281 else
282 {
283 ec = boost::system::error_code();
284 }
285 }
286 else
287 {
288 // Asynchronous operations have been started from more than one thread,
289 // so cancellation is not safe.
290 ec = boost::asio::error::operation_not_supported;
291 }
292
293 return ec;
294 }
295
296 size_t win_iocp_handle_service::do_write(
297 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
298 const boost::asio::const_buffer& buffer, boost::system::error_code& ec)
299 {
300 if (!is_open(impl))
301 {
302 ec = boost::asio::error::bad_descriptor;
303 return 0;
304 }
305
306 // A request to write 0 bytes on a handle is a no-op.
307 if (boost::asio::buffer_size(buffer) == 0)
308 {
309 ec = boost::system::error_code();
310 return 0;
311 }
312
313 overlapped_wrapper overlapped(ec);
314 if (ec)
315 {
316 return 0;
317 }
318
319 // Write the data.
320 overlapped.Offset = offset & 0xFFFFFFFF;
321 overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
322 BOOL ok = ::WriteFile(impl.handle_,
323 boost::asio::buffer_cast<LPCVOID>(buffer),
324 static_cast<DWORD>(boost::asio::buffer_size(buffer)), 0, &overlapped);
325 if (!ok)
326 {
327 DWORD last_error = ::GetLastError();
328 if (last_error != ERROR_IO_PENDING)
329 {
330 ec = boost::system::error_code(last_error,
331 boost::asio::error::get_system_category());
332 return 0;
333 }
334 }
335
336 // Wait for the operation to complete.
337 DWORD bytes_transferred = 0;
338 ok = ::GetOverlappedResult(impl.handle_,
339 &overlapped, &bytes_transferred, TRUE);
340 if (!ok)
341 {
342 DWORD last_error = ::GetLastError();
343 ec = boost::system::error_code(last_error,
344 boost::asio::error::get_system_category());
345 return 0;
346 }
347
348 ec = boost::system::error_code();
349 return bytes_transferred;
350 }
351
352 void win_iocp_handle_service::start_write_op(
353 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
354 const boost::asio::const_buffer& buffer, operation* op)
355 {
356 update_cancellation_thread_id(impl);
357 iocp_service_.work_started();
358
359 if (!is_open(impl))
360 {
361 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
362 }
363 else if (boost::asio::buffer_size(buffer) == 0)
364 {
365 // A request to write 0 bytes on a handle is a no-op.
366 iocp_service_.on_completion(op);
367 }
368 else
369 {
370 DWORD bytes_transferred = 0;
371 op->Offset = offset & 0xFFFFFFFF;
372 op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
373 BOOL ok = ::WriteFile(impl.handle_,
374 boost::asio::buffer_cast<LPCVOID>(buffer),
375 static_cast<DWORD>(boost::asio::buffer_size(buffer)),
376 &bytes_transferred, op);
377 DWORD last_error = ::GetLastError();
378 if (!ok && last_error != ERROR_IO_PENDING
379 && last_error != ERROR_MORE_DATA)
380 {
381 iocp_service_.on_completion(op, last_error, bytes_transferred);
382 }
383 else
384 {
385 iocp_service_.on_pending(op);
386 }
387 }
388 }
389
390 size_t win_iocp_handle_service::do_read(
391 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
392 const boost::asio::mutable_buffer& buffer, boost::system::error_code& ec)
393 {
394 if (!is_open(impl))
395 {
396 ec = boost::asio::error::bad_descriptor;
397 return 0;
398 }
399
400 // A request to read 0 bytes on a stream handle is a no-op.
401 if (boost::asio::buffer_size(buffer) == 0)
402 {
403 ec = boost::system::error_code();
404 return 0;
405 }
406
407 overlapped_wrapper overlapped(ec);
408 if (ec)
409 {
410 return 0;
411 }
412
413 // Read some data.
414 overlapped.Offset = offset & 0xFFFFFFFF;
415 overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
416 BOOL ok = ::ReadFile(impl.handle_,
417 boost::asio::buffer_cast<LPVOID>(buffer),
418 static_cast<DWORD>(boost::asio::buffer_size(buffer)), 0, &overlapped);
419 if (!ok)
420 {
421 DWORD last_error = ::GetLastError();
422 if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA)
423 {
424 if (last_error == ERROR_HANDLE_EOF)
425 {
426 ec = boost::asio::error::eof;
427 }
428 else
429 {
430 ec = boost::system::error_code(last_error,
431 boost::asio::error::get_system_category());
432 }
433 return 0;
434 }
435 }
436
437 // Wait for the operation to complete.
438 DWORD bytes_transferred = 0;
439 ok = ::GetOverlappedResult(impl.handle_,
440 &overlapped, &bytes_transferred, TRUE);
441 if (!ok)
442 {
443 DWORD last_error = ::GetLastError();
444 if (last_error == ERROR_HANDLE_EOF)
445 {
446 ec = boost::asio::error::eof;
447 }
448 else
449 {
450 ec = boost::system::error_code(last_error,
451 boost::asio::error::get_system_category());
452 }
453 return (last_error == ERROR_MORE_DATA) ? bytes_transferred : 0;
454 }
455
456 ec = boost::system::error_code();
457 return bytes_transferred;
458 }
459
460 void win_iocp_handle_service::start_read_op(
461 win_iocp_handle_service::implementation_type& impl, uint64_t offset,
462 const boost::asio::mutable_buffer& buffer, operation* op)
463 {
464 update_cancellation_thread_id(impl);
465 iocp_service_.work_started();
466
467 if (!is_open(impl))
468 {
469 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
470 }
471 else if (boost::asio::buffer_size(buffer) == 0)
472 {
473 // A request to read 0 bytes on a handle is a no-op.
474 iocp_service_.on_completion(op);
475 }
476 else
477 {
478 DWORD bytes_transferred = 0;
479 op->Offset = offset & 0xFFFFFFFF;
480 op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
481 BOOL ok = ::ReadFile(impl.handle_,
482 boost::asio::buffer_cast<LPVOID>(buffer),
483 static_cast<DWORD>(boost::asio::buffer_size(buffer)),
484 &bytes_transferred, op);
485 DWORD last_error = ::GetLastError();
486 if (!ok && last_error != ERROR_IO_PENDING
487 && last_error != ERROR_MORE_DATA)
488 {
489 iocp_service_.on_completion(op, last_error, bytes_transferred);
490 }
491 else
492 {
493 iocp_service_.on_pending(op);
494 }
495 }
496 }
497
498 void win_iocp_handle_service::update_cancellation_thread_id(
499 win_iocp_handle_service::implementation_type& impl)
500 {
501 if (impl.safe_cancellation_thread_id_ == 0)
502 impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
503 else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
504 impl.safe_cancellation_thread_id_ = ~DWORD(0);
505 }
506
507 void win_iocp_handle_service::close_for_destruction(implementation_type& impl)
508 {
509 if (is_open(impl))
510 {
511 BOOST_ASIO_HANDLER_OPERATION(("handle", &impl, "close"));
512
513 ::CloseHandle(impl.handle_);
514 impl.handle_ = INVALID_HANDLE_VALUE;
515 impl.safe_cancellation_thread_id_ = 0;
516 }
517 }
518
519 } // namespace detail
520 } // namespace asio
521 } // namespace boost
522
523 #include <boost/asio/detail/pop_options.hpp>
524
525 #endif // defined(BOOST_ASIO_HAS_IOCP)
526
527 #endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP