diff --git a/src/afd.c b/src/afd.c index abc3336..7373938 100644 --- a/src/afd.c +++ b/src/afd.c @@ -55,33 +55,18 @@ error: int afd_poll(HANDLE afd_helper_handle, AFD_POLL_INFO* poll_info, - OVERLAPPED* overlapped) { - IO_STATUS_BLOCK* iosb; - HANDLE event; - void* apc_context; + IO_STATUS_BLOCK* io_status_block) { NTSTATUS status; /* Blocking operation is not supported. */ - assert(overlapped != NULL); + assert(io_status_block != NULL); - iosb = (IO_STATUS_BLOCK*) &overlapped->Internal; - event = overlapped->hEvent; - - /* Do what other windows APIs would do: if hEvent has it's lowest bit set, - * don't post a completion to the completion port. */ - if ((uintptr_t) event & 1) { - event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1); - apc_context = NULL; - } else { - apc_context = overlapped; - } - - iosb->Status = STATUS_PENDING; + io_status_block->Status = STATUS_PENDING; status = NtDeviceIoControlFile(afd_helper_handle, - event, NULL, - apc_context, - iosb, + NULL, + io_status_block, + io_status_block, IOCTL_AFD_POLL, poll_info, sizeof *poll_info, @@ -95,3 +80,24 @@ int afd_poll(HANDLE afd_helper_handle, else return_set_error(-1, RtlNtStatusToDosError(status)); } + +int afd_cancel_poll(HANDLE afd_helper_handle, + IO_STATUS_BLOCK* io_status_block) { + NTSTATUS cancel_status; + IO_STATUS_BLOCK cancel_iosb; + + /* If the poll operation has already completed or has been cancelled earlier, + * there's nothing left for us to do. */ + if (io_status_block->Status != STATUS_PENDING) + return 0; + + cancel_status = + NtCancelIoFileEx(afd_helper_handle, io_status_block, &cancel_iosb); + + /* NtCancelIoFileEx() may return STATUS_NOT_FOUND if the operation completed + * just before calling NtCancelIoFileEx(). This is not an error. */ + if (cancel_status == STATUS_SUCCESS || cancel_status == STATUS_NOT_FOUND) + return 0; + else + return_set_error(-1, RtlNtStatusToDosError(cancel_status)); +} diff --git a/src/afd.h b/src/afd.h index cc49a44..844a09e 100644 --- a/src/afd.h +++ b/src/afd.h @@ -34,6 +34,8 @@ WEPOLL_INTERNAL int afd_create_helper_handle(HANDLE iocp_handle, WEPOLL_INTERNAL int afd_poll(HANDLE afd_helper_handle, AFD_POLL_INFO* poll_info, - OVERLAPPED* overlapped); + IO_STATUS_BLOCK* io_status_block); +WEPOLL_INTERNAL int afd_cancel_poll(HANDLE afd_helper_handle, + IO_STATUS_BLOCK* io_status_block); #endif /* WEPOLL_AFD_H_ */ diff --git a/src/nt.h b/src/nt.h index 07a9eb6..10a98d8 100644 --- a/src/nt.h +++ b/src/nt.h @@ -25,6 +25,10 @@ typedef NTSTATUS* PNTSTATUS; #define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) #endif +#ifndef STATUS_NOT_FOUND +#define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L) +#endif + typedef struct _IO_STATUS_BLOCK { NTSTATUS Status; ULONG_PTR Information; @@ -65,6 +69,13 @@ typedef struct _OBJECT_ATTRIBUTES { (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE) #define NT_NTDLL_IMPORT_LIST(X) \ + X(NTSTATUS, \ + NTAPI, \ + NtCancelIoFileEx, \ + (HANDLE FileHandle, \ + PIO_STATUS_BLOCK IoRequestToCancel, \ + PIO_STATUS_BLOCK IoStatusBlock)) \ + \ X(NTSTATUS, \ NTAPI, \ NtCreateFile, \ diff --git a/src/port.c b/src/port.c index c4c9241..afad1c8 100644 --- a/src/port.c +++ b/src/port.c @@ -4,6 +4,7 @@ #include #include "error.h" +#include "nt.h" #include "poll-group.h" #include "port.h" #include "queue.h" @@ -162,10 +163,11 @@ static int port__feed_events(port_state_t* port_state, DWORD i; for (i = 0; i < iocp_event_count; i++) { - OVERLAPPED* overlapped = iocp_events[i].lpOverlapped; + IO_STATUS_BLOCK* io_status_block = + (IO_STATUS_BLOCK*) iocp_events[i].lpOverlapped; struct epoll_event* ev = &epoll_events[epoll_event_count]; - epoll_event_count += sock_feed_event(port_state, overlapped, ev); + epoll_event_count += sock_feed_event(port_state, io_status_block, ev); } return epoll_event_count; diff --git a/src/sock.c b/src/sock.c index bad7d5c..e4539c3 100644 --- a/src/sock.c +++ b/src/sock.c @@ -26,7 +26,7 @@ typedef enum sock__poll_status { } sock__poll_status_t; typedef struct sock_state { - OVERLAPPED overlapped; + IO_STATUS_BLOCK io_status_block; AFD_POLL_INFO poll_info; queue_node_t queue_node; tree_node_t tree_node; @@ -51,16 +51,11 @@ static inline void sock__free(sock_state_t* sock_state) { } static int sock__cancel_poll(sock_state_t* sock_state) { - HANDLE afd_helper_handle = - poll_group_get_afd_helper_handle(sock_state->poll_group); assert(sock_state->poll_status == SOCK__POLL_PENDING); - /* CancelIoEx() may fail with ERROR_NOT_FOUND if the overlapped operation has - * already completed. This is not a problem and we proceed normally. */ - if (!HasOverlappedIoCompleted(&sock_state->overlapped) && - !CancelIoEx(afd_helper_handle, &sock_state->overlapped) && - GetLastError() != ERROR_NOT_FOUND) - return_map_error(-1); + if (afd_cancel_poll(poll_group_get_afd_helper_handle(sock_state->poll_group), + &sock_state->io_status_block) < 0) + return -1; sock_state->poll_status = SOCK__POLL_CANCELLED; sock_state->pending_events = 0; @@ -237,11 +232,9 @@ int sock_update(port_state_t* port_state, sock_state_t* sock_state) { sock_state->poll_info.Handles[0].Events = sock__epoll_events_to_afd_events(sock_state->user_events); - memset(&sock_state->overlapped, 0, sizeof sock_state->overlapped); - if (afd_poll(poll_group_get_afd_helper_handle(sock_state->poll_group), &sock_state->poll_info, - &sock_state->overlapped) < 0) { + &sock_state->io_status_block) < 0) { switch (GetLastError()) { case ERROR_IO_PENDING: /* Overlapped poll operation in progress; this is expected. */ @@ -269,10 +262,10 @@ int sock_update(port_state_t* port_state, sock_state_t* sock_state) { } int sock_feed_event(port_state_t* port_state, - OVERLAPPED* overlapped, + IO_STATUS_BLOCK* io_status_block, struct epoll_event* ev) { sock_state_t* sock_state = - container_of(overlapped, sock_state_t, overlapped); + container_of(io_status_block, sock_state_t, io_status_block); AFD_POLL_INFO* poll_info = &sock_state->poll_info; uint32_t epoll_events = 0; @@ -283,10 +276,10 @@ int sock_feed_event(port_state_t* port_state, /* Socket has been deleted earlier and can now be freed. */ return sock__delete(port_state, sock_state, false); - } else if ((NTSTATUS) overlapped->Internal == STATUS_CANCELLED) { + } else if (io_status_block->Status == STATUS_CANCELLED) { /* The poll request was cancelled by CancelIoEx. */ - } else if (!NT_SUCCESS(overlapped->Internal)) { + } else if (!NT_SUCCESS(io_status_block->Status)) { /* The overlapped request itself failed in an unexpected way. */ epoll_events = EPOLLERR; diff --git a/src/sock.h b/src/sock.h index c316ccd..e22f14e 100644 --- a/src/sock.h +++ b/src/sock.h @@ -2,6 +2,7 @@ #define WEPOLL_SOCK_H_ #include "internal.h" +#include "nt.h" #include "wepoll.h" #include "win.h" @@ -24,7 +25,7 @@ WEPOLL_INTERNAL int sock_set_event(port_state_t* port_state, WEPOLL_INTERNAL int sock_update(port_state_t* port_state, sock_state_t* sock_state); WEPOLL_INTERNAL int sock_feed_event(port_state_t* port_state, - OVERLAPPED* overlapped, + IO_STATUS_BLOCK* io_status_block, struct epoll_event* ev); WEPOLL_INTERNAL sock_state_t* sock_state_from_queue_node(