Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/asio/detail/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,17 @@
# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
#endif // !defined(ASIO_HAS_IOCP)

#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
# if defined(ASIO_HAS_IOCP)
# include <sdkddkver.h>
# if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_RS4)
# if defined(ASIO_ENABLE_IOCP_HIRES_TIMERS)
# define ASIO_HAS_IOCP_HIRES_TIMERS 1
# endif // defined(ASIO_ENABLE_IOCP_HIRES_TIMERS)
# endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_RS4)
# endif // defined(ASIO_HAS_IOCP)
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)

// On POSIX (and POSIX-like) platforms we need to include unistd.h in order to
// get access to the various platform feature macros, e.g. to be able to test
// for threads support.
Expand Down
98 changes: 98 additions & 0 deletions include/asio/detail/impl/win_iocp_io_context.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@

#include "asio/detail/push_options.hpp"

#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
#include <winternl.h>
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)

namespace asio {
namespace detail {

Expand Down Expand Up @@ -59,6 +63,7 @@ struct win_iocp_io_context::work_finished_on_block_exit
win_iocp_io_context* io_context_;
};

#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
struct win_iocp_io_context::timer_thread_function
{
void operator()()
Expand All @@ -77,6 +82,7 @@ struct win_iocp_io_context::timer_thread_function

win_iocp_io_context* io_context_;
};
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)

win_iocp_io_context::win_iocp_io_context(
asio::execution_context& ctx, bool own_thread)
Expand All @@ -103,6 +109,51 @@ win_iocp_io_context::win_iocp_io_context(
asio::detail::throw_error(ec, "iocp");
}

#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
if (FARPROC nt_create_wait_completion_packet_ptr = ::GetProcAddress(
::GetModuleHandleA("NTDLL"), "NtCreateWaitCompletionPacket"))
{
NtCreateWaitCompletionPacket_ =
reinterpret_cast<NtCreateWaitCompletionPacket_fn>(
reinterpret_cast<void*>(nt_create_wait_completion_packet_ptr));
}
else
{
DWORD last_error = ::GetLastError();
asio::error_code ec(last_error,
asio::error::get_system_category());
asio::detail::throw_error(ec, "timer");
}

if (FARPROC nt_associate_wait_completion_packet_ptr = ::GetProcAddress(
::GetModuleHandleA("NTDLL"), "NtAssociateWaitCompletionPacket")) {
NtAssociateWaitCompletionPacket_ =
reinterpret_cast<NtAssociateWaitCompletionPacket_fn>(
reinterpret_cast<void*>(nt_associate_wait_completion_packet_ptr));
}
else
{
DWORD last_error = ::GetLastError();
asio::error_code ec(last_error,
asio::error::get_system_category());
asio::detail::throw_error(ec, "timer");
}

if (FARPROC rtl_nt_status_to_dos_error_ptr = ::GetProcAddress(
::GetModuleHandleA("NTDLL"), "RtlNtStatusToDosError")) {
RtlNtStatusToDosError_ =
reinterpret_cast<RtlNtStatusToDosError_fn>(
reinterpret_cast<void*>(rtl_nt_status_to_dos_error_ptr));
}
else
{
DWORD last_error = ::GetLastError();
asio::error_code ec(last_error,
asio::error::get_system_category());
asio::detail::throw_error(ec, "timer");
}
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)

if (own_thread)
{
::InterlockedIncrement(&outstanding_work_);
Expand Down Expand Up @@ -149,12 +200,14 @@ void win_iocp_io_context::shutdown()
{
::InterlockedExchange(&shutdown_, 1);

#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
if (timer_thread_.joinable())
{
LARGE_INTEGER timeout;
timeout.QuadPart = 1;
::SetWaitableTimer(waitable_timer_.handle, &timeout, 1, 0, 0, FALSE);
}
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)

if (thread_.joinable())
{
Expand Down Expand Up @@ -192,7 +245,9 @@ void win_iocp_io_context::shutdown()
}
}

#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
timer_thread_.join();
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
}

asio::error_code win_iocp_io_context::register_handle(
Expand Down Expand Up @@ -520,6 +575,10 @@ size_t win_iocp_io_context::do_one(DWORD msec,
{
// We have been woken up to try to acquire responsibility for dispatching
// timers and completed operations.
#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
mutex::scoped_lock lock(dispatch_mutex_);
::InterlockedExchange(&dispatch_required_, 1);
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)
}
else
{
Expand Down Expand Up @@ -575,9 +634,26 @@ void win_iocp_io_context::do_add_timer_queue(timer_queue_base& queue)

timer_queues_.insert(&queue);

#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
if (!iocp_wait_handle_.handle)
{
NTSTATUS status = NtCreateWaitCompletionPacket_(&iocp_wait_handle_.handle, GENERIC_ALL, 0);
if (!NT_SUCCESS(status) || (iocp_wait_handle_.handle == 0)) {
DWORD win32_error = RtlNtStatusToDosError_(status);
asio::error_code ec(win32_error, asio::error::get_system_category());
asio::detail::throw_error(ec, "timer");
}
}
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)

if (!waitable_timer_.handle)
{
#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
waitable_timer_.handle = ::CreateWaitableTimerExW(
0, 0, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, SYNCHRONIZE | TIMER_MODIFY_STATE);
#else
waitable_timer_.handle = ::CreateWaitableTimer(0, FALSE, 0);
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)
if (waitable_timer_.handle == 0)
{
DWORD last_error = ::GetLastError();
Expand All @@ -591,13 +667,25 @@ void win_iocp_io_context::do_add_timer_queue(timer_queue_base& queue)
timeout.QuadPart *= 10;
::SetWaitableTimer(waitable_timer_.handle,
&timeout, max_timeout_msec, 0, 0, FALSE);
#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
NTSTATUS status = NtAssociateWaitCompletionPacket_(iocp_wait_handle_.handle, iocp_.handle,
waitable_timer_.handle, (PVOID)wake_for_dispatch,
0, 0, 0, 0);
if (!NT_SUCCESS(status)) {
DWORD win32_error = RtlNtStatusToDosError_(status);
asio::error_code ec(win32_error, asio::error::get_system_category());
asio::detail::throw_error(ec, "timer");
}
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)
}

#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
if (!timer_thread_.joinable())
{
timer_thread_function thread_function = { this };
timer_thread_ = thread(thread_function, 65536);
}
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
}

void win_iocp_io_context::do_remove_timer_queue(timer_queue_base& queue)
Expand All @@ -609,8 +697,11 @@ void win_iocp_io_context::do_remove_timer_queue(timer_queue_base& queue)

void win_iocp_io_context::update_timeout()
{

#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
if (timer_thread_.joinable())
{
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
// There's no point updating the waitable timer if the new timeout period
// exceeds the maximum timeout. In that case, we might as well wait for the
// existing period of the timer to expire.
Expand All @@ -622,8 +713,15 @@ void win_iocp_io_context::update_timeout()
timeout.QuadPart *= 10;
::SetWaitableTimer(waitable_timer_.handle,
&timeout, max_timeout_msec, 0, 0, FALSE);
#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
NtAssociateWaitCompletionPacket_(iocp_wait_handle_.handle, iocp_.handle,
waitable_timer_.handle,
(PVOID)wake_for_dispatch, 0, 0, 0, 0);
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)
}
#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
}
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
}

} // namespace detail
Expand Down
22 changes: 22 additions & 0 deletions include/asio/detail/win_iocp_io_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,18 @@ class win_iocp_io_context
typedef ULONG_PTR ulong_ptr_t;
#endif // defined(WINVER) && (WINVER < 0x0500)

#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
typedef LONG(NTAPI* NtCreateWaitCompletionPacket_fn)(PHANDLE, ACCESS_MASK, PVOID);

typedef LONG(NTAPI* NtAssociateWaitCompletionPacket_fn)(
HANDLE WaitCompletionPacketHandle, HANDLE IoCompletionHandle,
HANDLE TargetObjectHandle, PVOID KeyContext,
PVOID ApcContext, LONG IoStatus,
ULONG_PTR IoStatusInformation, PBOOLEAN AlreadySignaled);

typedef ULONG(NTAPI* RtlNtStatusToDosError_fn)(LONG Status);
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)

// Dequeues at most one operation from the I/O completion port, and then
// executes it. Returns the number of operations that were dequeued (i.e.
// either 0 or 1).
Expand Down Expand Up @@ -303,16 +315,26 @@ class win_iocp_io_context
struct thread_function;
friend struct thread_function;

#if !defined(ASIO_HAS_IOCP_HIRES_TIMERS)
// Function object for processing timeouts in a background thread.
struct timer_thread_function;
friend struct timer_thread_function;

// Background thread used for processing timeouts.
asio::detail::thread timer_thread_;
#endif // !defined(ASIO_HAS_IOCP_HIRES_TIMERS)

// A waitable timer object used for waiting for timeouts.
auto_handle waitable_timer_;

#if defined(ASIO_HAS_IOCP_HIRES_TIMERS)
auto_handle iocp_wait_handle_;

NtCreateWaitCompletionPacket_fn NtCreateWaitCompletionPacket_;
NtAssociateWaitCompletionPacket_fn NtAssociateWaitCompletionPacket_;
RtlNtStatusToDosError_fn RtlNtStatusToDosError_;
#endif // defined(ASIO_HAS_IOCP_HIRES_TIMERS)

// Non-zero if timers or completed operations need to be dispatched.
long dispatch_required_;

Expand Down