afd: use IO_STATUS_BLOCK instead of OVERLAPPED to track async poll ops

This reduces per-socket memory usage, as the OVERLAPPED structure
contains some fields that are never used.
This commit is contained in:
Bert Belder 2019-06-12 20:33:56 +02:00
parent a627f365fd
commit d5f8f5f1b1
No known key found for this signature in database
GPG Key ID: 7A77887B2E2ED461
6 changed files with 56 additions and 41 deletions

View File

@ -55,33 +55,18 @@ error:
int afd_poll(HANDLE afd_helper_handle, int afd_poll(HANDLE afd_helper_handle,
AFD_POLL_INFO* poll_info, AFD_POLL_INFO* poll_info,
OVERLAPPED* overlapped) { IO_STATUS_BLOCK* io_status_block) {
IO_STATUS_BLOCK* iosb;
HANDLE event;
void* apc_context;
NTSTATUS status; NTSTATUS status;
/* Blocking operation is not supported. */ /* Blocking operation is not supported. */
assert(overlapped != NULL); assert(io_status_block != NULL);
iosb = (IO_STATUS_BLOCK*) &overlapped->Internal; io_status_block->Status = STATUS_PENDING;
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;
status = NtDeviceIoControlFile(afd_helper_handle, status = NtDeviceIoControlFile(afd_helper_handle,
event,
NULL, NULL,
apc_context, NULL,
iosb, io_status_block,
io_status_block,
IOCTL_AFD_POLL, IOCTL_AFD_POLL,
poll_info, poll_info,
sizeof *poll_info, sizeof *poll_info,
@ -95,3 +80,24 @@ int afd_poll(HANDLE afd_helper_handle,
else else
return_set_error(-1, RtlNtStatusToDosError(status)); 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));
}

View File

@ -34,6 +34,8 @@ WEPOLL_INTERNAL int afd_create_helper_handle(HANDLE iocp_handle,
WEPOLL_INTERNAL int afd_poll(HANDLE afd_helper_handle, WEPOLL_INTERNAL int afd_poll(HANDLE afd_helper_handle,
AFD_POLL_INFO* poll_info, 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_ */ #endif /* WEPOLL_AFD_H_ */

View File

@ -25,6 +25,10 @@ typedef NTSTATUS* PNTSTATUS;
#define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) #define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L)
#endif #endif
#ifndef STATUS_NOT_FOUND
#define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L)
#endif
typedef struct _IO_STATUS_BLOCK { typedef struct _IO_STATUS_BLOCK {
NTSTATUS Status; NTSTATUS Status;
ULONG_PTR Information; ULONG_PTR Information;
@ -65,6 +69,13 @@ typedef struct _OBJECT_ATTRIBUTES {
(STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE) (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE)
#define NT_NTDLL_IMPORT_LIST(X) \ #define NT_NTDLL_IMPORT_LIST(X) \
X(NTSTATUS, \
NTAPI, \
NtCancelIoFileEx, \
(HANDLE FileHandle, \
PIO_STATUS_BLOCK IoRequestToCancel, \
PIO_STATUS_BLOCK IoStatusBlock)) \
\
X(NTSTATUS, \ X(NTSTATUS, \
NTAPI, \ NTAPI, \
NtCreateFile, \ NtCreateFile, \

View File

@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include "error.h" #include "error.h"
#include "nt.h"
#include "poll-group.h" #include "poll-group.h"
#include "port.h" #include "port.h"
#include "queue.h" #include "queue.h"
@ -162,10 +163,11 @@ static int port__feed_events(port_state_t* port_state,
DWORD i; DWORD i;
for (i = 0; i < iocp_event_count; 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]; 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; return epoll_event_count;

View File

@ -26,7 +26,7 @@ typedef enum sock__poll_status {
} sock__poll_status_t; } sock__poll_status_t;
typedef struct sock_state { typedef struct sock_state {
OVERLAPPED overlapped; IO_STATUS_BLOCK io_status_block;
AFD_POLL_INFO poll_info; AFD_POLL_INFO poll_info;
queue_node_t queue_node; queue_node_t queue_node;
tree_node_t tree_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) { 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); assert(sock_state->poll_status == SOCK__POLL_PENDING);
/* CancelIoEx() may fail with ERROR_NOT_FOUND if the overlapped operation has if (afd_cancel_poll(poll_group_get_afd_helper_handle(sock_state->poll_group),
* already completed. This is not a problem and we proceed normally. */ &sock_state->io_status_block) < 0)
if (!HasOverlappedIoCompleted(&sock_state->overlapped) && return -1;
!CancelIoEx(afd_helper_handle, &sock_state->overlapped) &&
GetLastError() != ERROR_NOT_FOUND)
return_map_error(-1);
sock_state->poll_status = SOCK__POLL_CANCELLED; sock_state->poll_status = SOCK__POLL_CANCELLED;
sock_state->pending_events = 0; 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_state->poll_info.Handles[0].Events =
sock__epoll_events_to_afd_events(sock_state->user_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), if (afd_poll(poll_group_get_afd_helper_handle(sock_state->poll_group),
&sock_state->poll_info, &sock_state->poll_info,
&sock_state->overlapped) < 0) { &sock_state->io_status_block) < 0) {
switch (GetLastError()) { switch (GetLastError()) {
case ERROR_IO_PENDING: case ERROR_IO_PENDING:
/* Overlapped poll operation in progress; this is expected. */ /* 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, int sock_feed_event(port_state_t* port_state,
OVERLAPPED* overlapped, IO_STATUS_BLOCK* io_status_block,
struct epoll_event* ev) { struct epoll_event* ev) {
sock_state_t* sock_state = 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; AFD_POLL_INFO* poll_info = &sock_state->poll_info;
uint32_t epoll_events = 0; 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. */ /* Socket has been deleted earlier and can now be freed. */
return sock__delete(port_state, sock_state, false); 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. */ /* 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. */ /* The overlapped request itself failed in an unexpected way. */
epoll_events = EPOLLERR; epoll_events = EPOLLERR;

View File

@ -2,6 +2,7 @@
#define WEPOLL_SOCK_H_ #define WEPOLL_SOCK_H_
#include "internal.h" #include "internal.h"
#include "nt.h"
#include "wepoll.h" #include "wepoll.h"
#include "win.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, WEPOLL_INTERNAL int sock_update(port_state_t* port_state,
sock_state_t* sock_state); sock_state_t* sock_state);
WEPOLL_INTERNAL int sock_feed_event(port_state_t* port_state, WEPOLL_INTERNAL int sock_feed_event(port_state_t* port_state,
OVERLAPPED* overlapped, IO_STATUS_BLOCK* io_status_block,
struct epoll_event* ev); struct epoll_event* ev);
WEPOLL_INTERNAL sock_state_t* sock_state_from_queue_node( WEPOLL_INTERNAL sock_state_t* sock_state_from_queue_node(