2
// win_iocp_handle_service.hpp
3
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com)
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)
12
#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP
13
#define ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP
15
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
17
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
19
#include "asio/detail/push_options.hpp"
21
#include "asio/detail/win_iocp_io_service_fwd.hpp"
23
#if defined(ASIO_HAS_IOCP)
25
#include "asio/detail/push_options.hpp"
26
#include <boost/cstdint.hpp>
27
#include "asio/detail/pop_options.hpp"
29
#include "asio/buffer.hpp"
30
#include "asio/error.hpp"
31
#include "asio/io_service.hpp"
32
#include "asio/detail/bind_handler.hpp"
33
#include "asio/detail/handler_alloc_helpers.hpp"
34
#include "asio/detail/handler_invoke_helpers.hpp"
35
#include "asio/detail/mutex.hpp"
36
#include "asio/detail/win_iocp_io_service.hpp"
41
class win_iocp_handle_service
42
: public asio::detail::service_base<win_iocp_handle_service>
45
// Base class for all operations.
46
typedef win_iocp_io_service::operation operation;
48
// The native type of a stream handle.
49
typedef HANDLE native_type;
51
// The implementation type of the stream handle.
52
class implementation_type
55
// Default constructor.
57
: handle_(INVALID_HANDLE_VALUE),
58
safe_cancellation_thread_id_(0),
65
// Only this service will have access to the internal values.
66
friend class win_iocp_handle_service;
68
// The native stream handle representation.
71
// The ID of the thread from which it is safe to cancel asynchronous
72
// operations. 0 means no asynchronous operations have been started yet.
73
// ~0 means asynchronous operations have been started from more than one
74
// thread, and cancellation is not supported for the handle.
75
DWORD safe_cancellation_thread_id_;
77
// Pointers to adjacent handle implementations in linked list.
78
implementation_type* next_;
79
implementation_type* prev_;
82
win_iocp_handle_service(asio::io_service& io_service)
83
: asio::detail::service_base<win_iocp_handle_service>(io_service),
84
iocp_service_(asio::use_service<win_iocp_io_service>(io_service)),
90
// Destroy all user-defined handler objects owned by the service.
91
void shutdown_service()
93
// Close all implementations, causing all operations to complete.
94
asio::detail::mutex::scoped_lock lock(mutex_);
95
implementation_type* impl = impl_list_;
98
close_for_destruction(*impl);
103
// Construct a new handle implementation.
104
void construct(implementation_type& impl)
106
impl.handle_ = INVALID_HANDLE_VALUE;
107
impl.safe_cancellation_thread_id_ = 0;
109
// Insert implementation into linked list of all implementations.
110
asio::detail::mutex::scoped_lock lock(mutex_);
111
impl.next_ = impl_list_;
114
impl_list_->prev_ = &impl;
118
// Destroy a handle implementation.
119
void destroy(implementation_type& impl)
121
close_for_destruction(impl);
123
// Remove implementation from linked list of all implementations.
124
asio::detail::mutex::scoped_lock lock(mutex_);
125
if (impl_list_ == &impl)
126
impl_list_ = impl.next_;
128
impl.prev_->next_ = impl.next_;
130
impl.next_->prev_= impl.prev_;
135
// Assign a native handle to a handle implementation.
136
asio::error_code assign(implementation_type& impl,
137
const native_type& native_handle, asio::error_code& ec)
141
ec = asio::error::already_open;
145
if (iocp_service_.register_handle(native_handle, ec))
148
impl.handle_ = native_handle;
149
ec = asio::error_code();
153
// Determine whether the handle is open.
154
bool is_open(const implementation_type& impl) const
156
return impl.handle_ != INVALID_HANDLE_VALUE;
159
// Destroy a handle implementation.
160
asio::error_code close(implementation_type& impl,
161
asio::error_code& ec)
165
if (!::CloseHandle(impl.handle_))
167
DWORD last_error = ::GetLastError();
168
ec = asio::error_code(last_error,
169
asio::error::get_system_category());
173
impl.handle_ = INVALID_HANDLE_VALUE;
174
impl.safe_cancellation_thread_id_ = 0;
177
ec = asio::error_code();
181
// Get the native handle representation.
182
native_type native(const implementation_type& impl) const
187
// Cancel all operations associated with the handle.
188
asio::error_code cancel(implementation_type& impl,
189
asio::error_code& ec)
193
ec = asio::error::bad_descriptor;
195
else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
196
::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
198
// The version of Windows supports cancellation from any thread.
199
typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
200
cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
201
if (!cancel_io_ex(impl.handle_, 0))
203
DWORD last_error = ::GetLastError();
204
if (last_error == ERROR_NOT_FOUND)
206
// ERROR_NOT_FOUND means that there were no operations to be
207
// cancelled. We swallow this error to match the behaviour on other
209
ec = asio::error_code();
213
ec = asio::error_code(last_error,
214
asio::error::get_system_category());
219
ec = asio::error_code();
222
else if (impl.safe_cancellation_thread_id_ == 0)
224
// No operations have been started, so there's nothing to cancel.
225
ec = asio::error_code();
227
else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
229
// Asynchronous operations have been started from the current thread only,
230
// so it is safe to try to cancel them using CancelIo.
231
if (!::CancelIo(impl.handle_))
233
DWORD last_error = ::GetLastError();
234
ec = asio::error_code(last_error,
235
asio::error::get_system_category());
239
ec = asio::error_code();
244
// Asynchronous operations have been started from more than one thread,
245
// so cancellation is not safe.
246
ec = asio::error::operation_not_supported;
252
class overlapped_wrapper
256
explicit overlapped_wrapper(asio::error_code& ec)
263
// Create a non-signalled manual-reset event, for GetOverlappedResult.
264
hEvent = ::CreateEvent(0, TRUE, FALSE, 0);
267
// As documented in GetQueuedCompletionStatus, setting the low order
268
// bit of this event prevents our synchronous writes from being treated
269
// as completion port events.
270
*reinterpret_cast<DWORD_PTR*>(&hEvent) |= 1;
274
DWORD last_error = ::GetLastError();
275
ec = asio::error_code(last_error,
276
asio::error::get_system_category());
280
~overlapped_wrapper()
284
::CloseHandle(hEvent);
289
// Write the given data. Returns the number of bytes written.
290
template <typename ConstBufferSequence>
291
size_t write_some(implementation_type& impl,
292
const ConstBufferSequence& buffers, asio::error_code& ec)
294
return write_some_at(impl, 0, buffers, ec);
297
// Write the given data at the specified offset. Returns the number of bytes
299
template <typename ConstBufferSequence>
300
size_t write_some_at(implementation_type& impl, boost::uint64_t offset,
301
const ConstBufferSequence& buffers, asio::error_code& ec)
305
ec = asio::error::bad_descriptor;
309
// Find first buffer of non-zero length.
310
asio::const_buffer buffer;
311
typename ConstBufferSequence::const_iterator iter = buffers.begin();
312
typename ConstBufferSequence::const_iterator end = buffers.end();
313
for (DWORD i = 0; iter != end; ++iter, ++i)
315
buffer = asio::const_buffer(*iter);
316
if (asio::buffer_size(buffer) != 0)
320
// A request to write 0 bytes on a handle is a no-op.
321
if (asio::buffer_size(buffer) == 0)
323
ec = asio::error_code();
327
overlapped_wrapper overlapped(ec);
334
overlapped.Offset = offset & 0xFFFFFFFF;
335
overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
336
BOOL ok = ::WriteFile(impl.handle_,
337
asio::buffer_cast<LPCVOID>(buffer),
338
static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped);
341
DWORD last_error = ::GetLastError();
342
if (last_error != ERROR_IO_PENDING)
344
ec = asio::error_code(last_error,
345
asio::error::get_system_category());
350
// Wait for the operation to complete.
351
DWORD bytes_transferred = 0;
352
ok = ::GetOverlappedResult(impl.handle_,
353
&overlapped, &bytes_transferred, TRUE);
356
DWORD last_error = ::GetLastError();
357
ec = asio::error_code(last_error,
358
asio::error::get_system_category());
361
ec = asio::error_code();
362
return bytes_transferred;
365
template <typename ConstBufferSequence, typename Handler>
366
class write_operation
370
write_operation(win_iocp_io_service& io_service,
371
const ConstBufferSequence& buffers, Handler handler)
372
: operation(io_service,
373
&write_operation<ConstBufferSequence, Handler>::do_completion_impl,
374
&write_operation<ConstBufferSequence, Handler>::destroy_impl),
375
work_(io_service.get_io_service()),
382
static void do_completion_impl(operation* op,
383
DWORD last_error, size_t bytes_transferred)
385
// Take ownership of the operation object.
386
typedef write_operation<ConstBufferSequence, Handler> op_type;
387
op_type* handler_op(static_cast<op_type*>(op));
388
typedef handler_alloc_traits<Handler, op_type> alloc_traits;
389
handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
391
#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
392
// Check whether buffers are still valid.
393
typename ConstBufferSequence::const_iterator iter
394
= handler_op->buffers_.begin();
395
typename ConstBufferSequence::const_iterator end
396
= handler_op->buffers_.end();
399
asio::const_buffer buffer(*iter);
400
asio::buffer_cast<const char*>(buffer);
403
#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
405
// Make a copy of the handler so that the memory can be deallocated before
406
// the upcall is made.
407
Handler handler(handler_op->handler_);
409
// Free the memory associated with the handler.
413
asio::error_code ec(last_error,
414
asio::error::get_system_category());
415
asio_handler_invoke_helpers::invoke(
416
bind_handler(handler, ec, bytes_transferred), &handler);
419
static void destroy_impl(operation* op)
421
// Take ownership of the operation object.
422
typedef write_operation<ConstBufferSequence, Handler> op_type;
423
op_type* handler_op(static_cast<op_type*>(op));
424
typedef handler_alloc_traits<Handler, op_type> alloc_traits;
425
handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
427
// A sub-object of the handler may be the true owner of the memory
428
// associated with the handler. Consequently, a local copy of the handler
429
// is required to ensure that any owning sub-object remains valid until
430
// after we have deallocated the memory here.
431
Handler handler(handler_op->handler_);
434
// Free the memory associated with the handler.
438
asio::io_service::work work_;
439
ConstBufferSequence buffers_;
443
// Start an asynchronous write. The data being written must be valid for the
444
// lifetime of the asynchronous operation.
445
template <typename ConstBufferSequence, typename Handler>
446
void async_write_some(implementation_type& impl,
447
const ConstBufferSequence& buffers, Handler handler)
449
async_write_some_at(impl, 0, buffers, handler);
452
// Start an asynchronous write at a specified offset. The data being written
453
// must be valid for the lifetime of the asynchronous operation.
454
template <typename ConstBufferSequence, typename Handler>
455
void async_write_some_at(implementation_type& impl, boost::uint64_t offset,
456
const ConstBufferSequence& buffers, Handler handler)
460
this->get_io_service().post(bind_handler(handler,
461
asio::error::bad_descriptor, 0));
465
// Update the ID of the thread from which cancellation is safe.
466
if (impl.safe_cancellation_thread_id_ == 0)
467
impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
468
else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
469
impl.safe_cancellation_thread_id_ = ~DWORD(0);
471
// Allocate and construct an operation to wrap the handler.
472
typedef write_operation<ConstBufferSequence, Handler> value_type;
473
typedef handler_alloc_traits<Handler, value_type> alloc_traits;
474
raw_handler_ptr<alloc_traits> raw_ptr(handler);
475
handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, buffers, handler);
477
// Find first buffer of non-zero length.
478
asio::const_buffer buffer;
479
typename ConstBufferSequence::const_iterator iter = buffers.begin();
480
typename ConstBufferSequence::const_iterator end = buffers.end();
481
for (DWORD i = 0; iter != end; ++iter, ++i)
483
buffer = asio::const_buffer(*iter);
484
if (asio::buffer_size(buffer) != 0)
488
// A request to write 0 bytes on a handle is a no-op.
489
if (asio::buffer_size(buffer) == 0)
491
asio::io_service::work work(this->get_io_service());
493
asio::error_code error;
494
iocp_service_.post(bind_handler(handler, error, 0));
499
DWORD bytes_transferred = 0;
500
ptr.get()->Offset = offset & 0xFFFFFFFF;
501
ptr.get()->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
502
BOOL ok = ::WriteFile(impl.handle_,
503
asio::buffer_cast<LPCVOID>(buffer),
504
static_cast<DWORD>(asio::buffer_size(buffer)),
505
&bytes_transferred, ptr.get());
506
DWORD last_error = ::GetLastError();
508
// Check if the operation completed immediately.
509
if (!ok && last_error != ERROR_IO_PENDING)
511
asio::io_service::work work(this->get_io_service());
513
asio::error_code ec(last_error,
514
asio::error::get_system_category());
515
iocp_service_.post(bind_handler(handler, ec, bytes_transferred));
523
// Read some data. Returns the number of bytes received.
524
template <typename MutableBufferSequence>
525
size_t read_some(implementation_type& impl,
526
const MutableBufferSequence& buffers, asio::error_code& ec)
528
return read_some_at(impl, 0, buffers, ec);
531
// Read some data at a specified offset. Returns the number of bytes received.
532
template <typename MutableBufferSequence>
533
size_t read_some_at(implementation_type& impl, boost::uint64_t offset,
534
const MutableBufferSequence& buffers, asio::error_code& ec)
538
ec = asio::error::bad_descriptor;
542
// Find first buffer of non-zero length.
543
asio::mutable_buffer buffer;
544
typename MutableBufferSequence::const_iterator iter = buffers.begin();
545
typename MutableBufferSequence::const_iterator end = buffers.end();
546
for (DWORD i = 0; iter != end; ++iter, ++i)
548
buffer = asio::mutable_buffer(*iter);
549
if (asio::buffer_size(buffer) != 0)
553
// A request to read 0 bytes on a stream handle is a no-op.
554
if (asio::buffer_size(buffer) == 0)
556
ec = asio::error_code();
560
overlapped_wrapper overlapped(ec);
567
overlapped.Offset = offset & 0xFFFFFFFF;
568
overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
569
BOOL ok = ::ReadFile(impl.handle_,
570
asio::buffer_cast<LPVOID>(buffer),
571
static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped);
574
DWORD last_error = ::GetLastError();
575
if (last_error != ERROR_IO_PENDING)
577
if (last_error == ERROR_HANDLE_EOF)
579
ec = asio::error::eof;
583
ec = asio::error_code(last_error,
584
asio::error::get_system_category());
590
// Wait for the operation to complete.
591
DWORD bytes_transferred = 0;
592
ok = ::GetOverlappedResult(impl.handle_,
593
&overlapped, &bytes_transferred, TRUE);
596
DWORD last_error = ::GetLastError();
597
if (last_error == ERROR_HANDLE_EOF)
599
ec = asio::error::eof;
603
ec = asio::error_code(last_error,
604
asio::error::get_system_category());
608
ec = asio::error_code();
609
return bytes_transferred;
612
template <typename MutableBufferSequence, typename Handler>
617
read_operation(win_iocp_io_service& io_service,
618
const MutableBufferSequence& buffers, Handler handler)
619
: operation(io_service,
621
MutableBufferSequence, Handler>::do_completion_impl,
623
MutableBufferSequence, Handler>::destroy_impl),
624
work_(io_service.get_io_service()),
631
static void do_completion_impl(operation* op,
632
DWORD last_error, size_t bytes_transferred)
634
// Take ownership of the operation object.
635
typedef read_operation<MutableBufferSequence, Handler> op_type;
636
op_type* handler_op(static_cast<op_type*>(op));
637
typedef handler_alloc_traits<Handler, op_type> alloc_traits;
638
handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
640
#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
641
// Check whether buffers are still valid.
642
typename MutableBufferSequence::const_iterator iter
643
= handler_op->buffers_.begin();
644
typename MutableBufferSequence::const_iterator end
645
= handler_op->buffers_.end();
648
asio::mutable_buffer buffer(*iter);
649
asio::buffer_cast<char*>(buffer);
652
#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
654
// Check for the end-of-file condition.
655
asio::error_code ec(last_error,
656
asio::error::get_system_category());
657
if (!ec && bytes_transferred == 0 || last_error == ERROR_HANDLE_EOF)
659
ec = asio::error::eof;
662
// Make a copy of the handler so that the memory can be deallocated before
663
// the upcall is made.
664
Handler handler(handler_op->handler_);
666
// Free the memory associated with the handler.
670
asio_handler_invoke_helpers::invoke(
671
bind_handler(handler, ec, bytes_transferred), &handler);
674
static void destroy_impl(operation* op)
676
// Take ownership of the operation object.
677
typedef read_operation<MutableBufferSequence, Handler> op_type;
678
op_type* handler_op(static_cast<op_type*>(op));
679
typedef asio::detail::handler_alloc_traits<
680
Handler, op_type> alloc_traits;
681
asio::detail::handler_ptr<alloc_traits> ptr(
682
handler_op->handler_, handler_op);
684
// A sub-object of the handler may be the true owner of the memory
685
// associated with the handler. Consequently, a local copy of the handler
686
// is required to ensure that any owning sub-object remains valid until
687
// after we have deallocated the memory here.
688
Handler handler(handler_op->handler_);
691
// Free the memory associated with the handler.
695
asio::io_service::work work_;
696
MutableBufferSequence buffers_;
700
// Start an asynchronous read. The buffer for the data being received must be
701
// valid for the lifetime of the asynchronous operation.
702
template <typename MutableBufferSequence, typename Handler>
703
void async_read_some(implementation_type& impl,
704
const MutableBufferSequence& buffers, Handler handler)
706
async_read_some_at(impl, 0, buffers, handler);
709
// Start an asynchronous read at a specified offset. The buffer for the data
710
// being received must be valid for the lifetime of the asynchronous
712
template <typename MutableBufferSequence, typename Handler>
713
void async_read_some_at(implementation_type& impl, boost::uint64_t offset,
714
const MutableBufferSequence& buffers, Handler handler)
718
this->get_io_service().post(bind_handler(handler,
719
asio::error::bad_descriptor, 0));
723
// Update the ID of the thread from which cancellation is safe.
724
if (impl.safe_cancellation_thread_id_ == 0)
725
impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
726
else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
727
impl.safe_cancellation_thread_id_ = ~DWORD(0);
729
// Allocate and construct an operation to wrap the handler.
730
typedef read_operation<MutableBufferSequence, Handler> value_type;
731
typedef handler_alloc_traits<Handler, value_type> alloc_traits;
732
raw_handler_ptr<alloc_traits> raw_ptr(handler);
733
handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, buffers, handler);
735
// Find first buffer of non-zero length.
736
asio::mutable_buffer buffer;
737
typename MutableBufferSequence::const_iterator iter = buffers.begin();
738
typename MutableBufferSequence::const_iterator end = buffers.end();
739
for (DWORD i = 0; iter != end; ++iter, ++i)
741
buffer = asio::mutable_buffer(*iter);
742
if (asio::buffer_size(buffer) != 0)
746
// A request to receive 0 bytes on a stream handle is a no-op.
747
if (asio::buffer_size(buffer) == 0)
749
asio::io_service::work work(this->get_io_service());
751
asio::error_code error;
752
iocp_service_.post(bind_handler(handler, error, 0));
757
DWORD bytes_transferred = 0;
758
ptr.get()->Offset = offset & 0xFFFFFFFF;
759
ptr.get()->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
760
BOOL ok = ::ReadFile(impl.handle_,
761
asio::buffer_cast<LPVOID>(buffer),
762
static_cast<DWORD>(asio::buffer_size(buffer)),
763
&bytes_transferred, ptr.get());
764
DWORD last_error = ::GetLastError();
765
if (!ok && last_error != ERROR_IO_PENDING)
767
asio::io_service::work work(this->get_io_service());
769
asio::error_code ec(last_error,
770
asio::error::get_system_category());
771
iocp_service_.post(bind_handler(handler, ec, bytes_transferred));
780
// Prevent the use of the null_buffers type with this service.
781
size_t write_some(implementation_type& impl,
782
const null_buffers& buffers, asio::error_code& ec);
783
size_t write_some_at(implementation_type& impl, boost::uint64_t offset,
784
const null_buffers& buffers, asio::error_code& ec);
785
template <typename Handler>
786
void async_write_some(implementation_type& impl,
787
const null_buffers& buffers, Handler handler);
788
template <typename Handler>
789
void async_write_some_at(implementation_type& impl, boost::uint64_t offset,
790
const null_buffers& buffers, Handler handler);
791
size_t read_some(implementation_type& impl,
792
const null_buffers& buffers, asio::error_code& ec);
793
size_t read_some_at(implementation_type& impl, boost::uint64_t offset,
794
const null_buffers& buffers, asio::error_code& ec);
795
template <typename Handler>
796
void async_read_some(implementation_type& impl,
797
const null_buffers& buffers, Handler handler);
798
template <typename Handler>
799
void async_read_some_at(implementation_type& impl, boost::uint64_t offset,
800
const null_buffers& buffers, Handler handler);
802
// Helper function to close a handle when the associated object is being
804
void close_for_destruction(implementation_type& impl)
808
::CloseHandle(impl.handle_);
809
impl.handle_ = INVALID_HANDLE_VALUE;
810
impl.safe_cancellation_thread_id_ = 0;
814
// The IOCP service used for running asynchronous operations and dispatching
816
win_iocp_io_service& iocp_service_;
818
// Mutex to protect access to the linked list of implementations.
819
asio::detail::mutex mutex_;
821
// The head of a linked list of all implementations.
822
implementation_type* impl_list_;
825
} // namespace detail
828
#endif // defined(ASIO_HAS_IOCP)
830
#include "asio/detail/pop_options.hpp"
832
#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP