Skip to content

Commit

Permalink
afd: use IO_STATUS_BLOCK instead of OVERLAPPED to track async poll ops
Browse files Browse the repository at this point in the history
This reduces per-socket memory usage, as the OVERLAPPED structure
contains some fields that are never used.
  • Loading branch information
piscisaureus committed Jun 12, 2019
1 parent fee813a commit 8eacd48
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 41 deletions.
48 changes: 27 additions & 21 deletions src/afd.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,33 +55,18 @@ int afd_create_helper_handle(HANDLE iocp_handle,

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,
Expand All @@ -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));
}
4 changes: 3 additions & 1 deletion src/afd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
12 changes: 12 additions & 0 deletions src/nt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,6 +69,14 @@ 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, \
Expand Down
6 changes: 4 additions & 2 deletions src/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string.h>

#include "error.h"
#include "nt.h"
#include "poll-group.h"
#include "port.h"
#include "queue.h"
Expand Down Expand Up @@ -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;
Expand Down
25 changes: 9 additions & 16 deletions src/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion src/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define WEPOLL_SOCK_H_

#include "internal.h"
#include "nt.h"
#include "wepoll.h"
#include "win.h"

Expand All @@ -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(
Expand Down

0 comments on commit 8eacd48

Please sign in to comment.