2
// win_iocp_io_service.hpp
3
// ~~~~~~~~~~~~~~~~~~~~~~~
5
// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com)
7
// Distributed under the Boost Software License, Version 1.0. (See accompanying
8
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11
#ifndef ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
12
#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
14
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
16
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18
#include "asio/detail/push_options.hpp"
20
#include "asio/detail/win_iocp_io_service_fwd.hpp"
22
#if defined(ASIO_HAS_IOCP)
24
#include "asio/detail/push_options.hpp"
26
#include <boost/throw_exception.hpp>
27
#include "asio/detail/pop_options.hpp"
29
#include "asio/io_service.hpp"
30
#include "asio/system_error.hpp"
31
#include "asio/detail/call_stack.hpp"
32
#include "asio/detail/handler_alloc_helpers.hpp"
33
#include "asio/detail/handler_invoke_helpers.hpp"
34
#include "asio/detail/service_base.hpp"
35
#include "asio/detail/socket_types.hpp"
36
#include "asio/detail/win_iocp_operation.hpp"
41
class win_iocp_io_service
42
: public asio::detail::service_base<win_iocp_io_service>
45
// Base class for all operations.
46
typedef win_iocp_operation operation;
49
win_iocp_io_service(asio::io_service& io_service)
50
: asio::detail::service_base<win_iocp_io_service>(io_service),
58
void init(size_t concurrency_hint)
60
iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0,
61
static_cast<DWORD>((std::min<size_t>)(concurrency_hint, DWORD(~0))));
64
DWORD last_error = ::GetLastError();
66
asio::error_code(last_error, asio::native_ecat),
68
boost::throw_exception(e);
72
// Destroy all user-defined handler objects owned by the service.
73
void shutdown_service()
75
::InterlockedExchange(&shutdown_, 1);
79
DWORD bytes_transferred = 0;
81
DWORD completion_key = 0;
83
DWORD_PTR completion_key = 0;
85
LPOVERLAPPED overlapped = 0;
87
BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle,
88
&bytes_transferred, &completion_key, &overlapped, 0);
89
DWORD last_error = ::GetLastError();
90
if (!ok && overlapped == 0 && last_error == WAIT_TIMEOUT)
93
static_cast<operation*>(overlapped)->destroy();
97
// Register a handle with the IO completion port.
98
void register_handle(HANDLE handle)
100
::CreateIoCompletionPort(handle, iocp_.handle, 0, 0);
103
// Run the event loop until stopped or no more work.
104
size_t run(asio::error_code& ec)
106
if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
108
ec = asio::error_code();
112
call_stack<win_iocp_io_service>::context ctx(this);
115
while (do_one(true, ec))
116
if (n != (std::numeric_limits<size_t>::max)())
121
// Run until stopped or one operation is performed.
122
size_t run_one(asio::error_code& ec)
124
if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
126
ec = asio::error_code();
130
call_stack<win_iocp_io_service>::context ctx(this);
132
return do_one(true, ec);
135
// Poll for operations without blocking.
136
size_t poll(asio::error_code& ec)
138
if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
140
ec = asio::error_code();
144
call_stack<win_iocp_io_service>::context ctx(this);
147
while (do_one(false, ec))
148
if (n != (std::numeric_limits<size_t>::max)())
153
// Poll for one operation without blocking.
154
size_t poll_one(asio::error_code& ec)
156
if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
158
ec = asio::error_code();
162
call_stack<win_iocp_io_service>::context ctx(this);
164
return do_one(false, ec);
167
// Stop the event processing loop.
170
if (::InterlockedExchange(&stopped_, 1) == 0)
172
if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
174
DWORD last_error = ::GetLastError();
175
asio::system_error e(
176
asio::error_code(last_error, asio::native_ecat),
178
boost::throw_exception(e);
183
// Reset in preparation for a subsequent run invocation.
186
::InterlockedExchange(&stopped_, 0);
189
// Notify that some work has started.
192
::InterlockedIncrement(&outstanding_work_);
195
// Notify that some work has finished.
198
if (::InterlockedDecrement(&outstanding_work_) == 0)
202
// Request invocation of the given handler.
203
template <typename Handler>
204
void dispatch(Handler handler)
206
if (call_stack<win_iocp_io_service>::contains(this))
207
asio_handler_invoke_helpers::invoke(handler, &handler);
212
// Request invocation of the given handler and return immediately.
213
template <typename Handler>
214
void post(Handler handler)
216
// If the service has been shut down we silently discard the handler.
217
if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
220
// Allocate and construct an operation to wrap the handler.
221
typedef handler_operation<Handler> value_type;
222
typedef handler_alloc_traits<Handler, value_type> alloc_traits;
223
raw_handler_ptr<alloc_traits> raw_ptr(handler);
224
handler_ptr<alloc_traits> ptr(raw_ptr, *this, handler);
226
// Enqueue the operation on the I/O completion port.
227
if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, ptr.get()))
229
DWORD last_error = ::GetLastError();
230
asio::system_error e(
231
asio::error_code(last_error, asio::native_ecat),
233
boost::throw_exception(e);
236
// Operation has been successfully posted.
240
// Request invocation of the given OVERLAPPED-derived operation.
241
void post_completion(win_iocp_operation* op, DWORD op_last_error,
242
DWORD bytes_transferred)
244
// Enqueue the operation on the I/O completion port.
245
if (!::PostQueuedCompletionStatus(iocp_.handle,
246
bytes_transferred, op_last_error, op))
248
DWORD last_error = ::GetLastError();
249
asio::system_error e(
250
asio::error_code(last_error, asio::native_ecat),
252
boost::throw_exception(e);
257
// Dequeues at most one operation from the I/O completion port, and then
258
// executes it. Returns the number of operations that were dequeued (i.e.
260
size_t do_one(bool block, asio::error_code& ec)
264
// Get the next operation from the queue.
265
DWORD bytes_transferred = 0;
266
#if (WINVER < 0x0500)
267
DWORD completion_key = 0;
269
DWORD_PTR completion_key = 0;
271
LPOVERLAPPED overlapped = 0;
273
BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
274
&completion_key, &overlapped, block ? 1000 : 0);
275
DWORD last_error = ::GetLastError();
277
if (!ok && overlapped == 0)
279
if (block && last_error == WAIT_TIMEOUT)
281
ec = asio::error_code();
287
// We may have been passed a last_error value in the completion_key.
290
last_error = completion_key;
293
// Ensure that the io_service does not exit due to running out of work
294
// while we make the upcall.
295
auto_work work(*this);
297
// Dispatch the operation.
298
operation* op = static_cast<operation*>(overlapped);
299
op->do_completion(last_error, bytes_transferred);
301
ec = asio::error_code();
306
// The stopped_ flag is always checked to ensure that any leftover
307
// interrupts from a previous run invocation are ignored.
308
if (::InterlockedExchangeAdd(&stopped_, 0) != 0)
310
// Wake up next thread that is blocked on GetQueuedCompletionStatus.
311
if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
313
DWORD last_error = ::GetLastError();
314
ec = asio::error_code(last_error,
319
ec = asio::error_code();
328
auto_work(win_iocp_io_service& io_service)
329
: io_service_(io_service)
331
io_service_.work_started();
336
io_service_.work_finished();
340
win_iocp_io_service& io_service_;
343
template <typename Handler>
344
struct handler_operation
347
handler_operation(win_iocp_io_service& io_service,
349
: operation(&handler_operation<Handler>::do_completion_impl,
350
&handler_operation<Handler>::destroy_impl),
351
io_service_(io_service),
354
io_service_.work_started();
359
io_service_.work_finished();
363
// Prevent copying and assignment.
364
handler_operation(const handler_operation&);
365
void operator=(const handler_operation&);
367
static void do_completion_impl(operation* op, DWORD, size_t)
369
// Take ownership of the operation object.
370
typedef handler_operation<Handler> op_type;
371
op_type* handler_op(static_cast<op_type*>(op));
372
typedef handler_alloc_traits<Handler, op_type> alloc_traits;
373
handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
375
// Make a copy of the handler so that the memory can be deallocated before
376
// the upcall is made.
377
Handler handler(handler_op->handler_);
379
// Free the memory associated with the handler.
383
asio_handler_invoke_helpers::invoke(handler, &handler);
386
static void destroy_impl(operation* op)
388
// Take ownership of the operation object.
389
typedef handler_operation<Handler> op_type;
390
op_type* handler_op(static_cast<op_type*>(op));
391
typedef handler_alloc_traits<Handler, op_type> alloc_traits;
392
handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
395
win_iocp_io_service& io_service_;
399
// The IO completion port used for queueing operations.
403
iocp_holder() : handle(0) {}
404
~iocp_holder() { if (handle) ::CloseHandle(handle); }
407
// The count of unfinished work.
408
long outstanding_work_;
410
// Flag to indicate whether the event loop has been stopped.
413
// Flag to indicate whether the service has been shut down.
417
} // namespace detail
420
#endif // defined(ASIO_HAS_IOCP)
422
#include "asio/detail/pop_options.hpp"
424
#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP